/***********************************************************************
 * 
 *  Copyright (C) 2005-2006 Novell, Inc. All Rights Reserved.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; version 2.1
 *  of the License.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, Novell, Inc.
 * 
 *  To contact Novell about this file by physical or electronic mail, 
 *  you may find current contact information at www.novell.com.
 * 
 ***********************************************************************/

using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Collections.Specialized;
using System.Collections;

using Novell.CASA.MiCasa.Communication;
using Novell.CASA.MiCasa.Common;

namespace Novell.CASA
{
	/// <summary>
	/// Summary description for NativeCalls.
	/// </summary>
	public class NativeCalls
	{
		private static SSCS_KEYCHAIN_ID_T	DefaultKeychainID = null;		
		private static int SSCS_CRED_TYPE_BASIC_F  = 1;
		private static int USERNAME_LEN = 256;
		private static int PASSWORD_LEN = 128;
		private static bool USE_OBJECT_SERIALIZATION = true;

		public NativeCalls()
		{
			//
			// TODO: Add constructor logic here
			//
			if (DefaultKeychainID == null)
			{
				DefaultKeychainID = new SSCS_KEYCHAIN_ID_T();		
			}
		}

		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
			public class SSCS_PASSWORD_T
		{
			public uint			pwordType;
			public uint			pwordLen;		// * enhanced protection len & pword to set
			//			[MarshalAs(UnmanagedType.LPStr, SizeConst = 128)]
			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
			public string		pword;			// * should be passed in # of chars
		} ;

		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
			public class SSCS_PASSCODE_T
		{
			public uint passcodeType;
			public IntPtr passcodeHandle;    
		}

		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
			public class SSCS_KEYCHAIN_ID_T
		{
			public int		len;		
			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 512)]
			public string	keychainID;
		};

		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
			public class SSCS_SH_SECRET_ID_T
		{
			public int  	type;				// The shared secret type i.e. SS_App  or  SS_CredSet
			public int  	len;				// except that it excludes the header and is not escaped.
			[MarshalAs(UnmanagedType.ByValTStr , SizeConst = 512)]
			public string	name;				// The shared secret name. This is the same as the identifier 
		};

		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
			public class SSCS_SECRET_STORE_ID_T
		{
			public int			version;						// * max id len in bytes
			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 512)]
			public string			id;							// * should be passed in # of chars
		};

		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
			public class SSCS_SECRET_ID_T
		{
			public int		len;						// * max id len in bytes
			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 512)]
			public string	id; 						// * should be passed in # of chars
		};

		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
			public class SSCS_HINT_T
		{
			ulong		hintLen;		
			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
			string		hint;	//* should be passed in # of chars
		};

		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
			public class SSCS_EXT_T
		{
			public int			extID; 		// defined to identify the extension 
			public int			version;	// defined as the version of the specified extension
			public IntPtr		ext;		// points to the actual extension
		} ;
		/*
				[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
					public class SSCS_SECRETSTORE_INF_T
				{
					uint 	numKeyChains;
					uint	enumBufsize; 
				};
		*/
		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
			public class SSCS_SRCH_KEY_T
		{
			public uint		srchKeyLen;	
			[MarshalAs(UnmanagedType.LPStr, SizeConst = 128)]
			public string	srchKey; //[NSSCS_MAX_SRCH_KEY_LEN];	//* should be passed in # of chars
		} ;

		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
			public class SSCS_SECRET_ID_LIST_T
		{
			public uint			enumHandle;
			public uint			returnedIDs;
			public IntPtr		secretIDList; //* an array of secretID structures						
		};

		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
			public class SSCS_SH_SECRET_ID_LIST_T
		{
			public uint			enumHandle;
			public uint			returnedIDs;			
			public IntPtr		secretIDList; //* an array of secretID structures						
		};

		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
			public class SSCS_CONTEXT_T
		{
			public int								version;
			public int								flags;
			public SSCS_SECRET_ID_T					ssid;
			public UInt64							ssHandle;			
		};

		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
			public class SSCS_READEXT_T
		{
			public uint		statFlags;		//* OUT - return flags on the secret
			public uint		crtStamp;		//* OUT - secret creation time stamp
			public uint		latStamp;		//* OUT - last accessed time stamp (optional)
			public uint		lmtStamp;		//* OUT - last modified time stamp
		};

		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
			public class SSCS_SECRETSTORE_INFO_T
		{
			public UInt32 statFlags;
			public UInt32 secretCount;
			public UInt32 lockCount;
			public UInt32 enumBufLen;
			public UInt32 hidSecCount;
			public UInt32 clientVersion;
			public UInt32 serverVersion;
			public UInt32 serverCryptoStrength;
			public UInt32 clientCryptoStrength;
			public UInt32 unlockTStamp;
			public UInt32 admnDNLen;
			[MarshalAs(UnmanagedType.ByValArray, SizeConst = 516)]
			public string admnDN; //[NSSS_MAX_DN_LEN];
			public UInt32 hintLen;
			[MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
			public string hint; //[NSSS_MAX_MP_PWORD_HINT_LEN]];
		};

		/*
				[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
					public class SSCS_BASIC_CREDENTIAL
				{
					public uint			unFlags;
					public uint			unLen;
					[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
					public string		username;			
					public uint		pwordLen;
					[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
					public string		password;			
				}
		*/
		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
			public class SSCS_BASIC_CREDENTIAL_UTF8
		{
			public uint			unFlags;
			public uint			unLen;
			[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
			public byte[]		username;			
			public uint			pwordLen;
			[MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
			public byte[]		password;			
		}

		// * The following functions will be exposed as exported API:
		// **************************************************************
		// * These function calls will utilize the Support Functions for 
		// * populating or extracting data from a Shared Secret.
		// **************************************************************

		private const string NDK_LIBRARY = "micasa";

		[DllImport(NDK_LIBRARY)]
		public static extern int miCASARemoveCredential
			(			
			[In]		uint	 				ssFlags, 				// IN
			[In]		SSCS_SECRET_ID_T		appSecretID, 
			[In]		SSCS_SECRET_ID_T		sharedSecretID, 					
			[In, Out]	SSCS_EXT_T				ext
			);



		[DllImport(NDK_LIBRARY)]
		public static extern int miCASAGetCredential
			(			
			[In]		uint	 				ssFlags, 				// IN
			[In]		SSCS_SECRET_ID_T		appSecretID, 
			[In]		SSCS_SECRET_ID_T		sharedSecretID, 						 
			[In, Out]   ref uint				credentialType,			// IN
			[In, Out]	IntPtr					pCredential,		
			[In, Out]	SSCS_EXT_T				ext
			);


		[DllImport(NDK_LIBRARY)]
		public static extern int miCASASetCredential
			(			
			[In]		uint	 				ssFlags, 				// IN
			[In]		SSCS_SECRET_ID_T		appSecretID, 
			[In]		SSCS_SECRET_ID_T		sharedSecretID, 						 
			int									credentialType,			// IN
			[In]		IntPtr					credential,			
			[In, Out]	SSCS_EXT_T				ext
			);
		[DllImport(NDK_LIBRARY)]
		public static extern int miCASASetMasterPasscode
			(
			[In] uint            ssFlags,
			[In] SSCS_PASSCODE_T passcode,
			[In] SSCS_EXT_T      ext
			);

		[DllImport(NDK_LIBRARY)]
		public static extern int miCASAIsSecretPersistent
			(			
			[In] uint ssFlags,
			[In] SSCS_SECRET_ID_T secretID, 
			[In, Out] SSCS_EXT_T ext
			);
		
		[DllImport(NDK_LIBRARY)]
		public static extern IntPtr miCASAOpenSecretStoreCache
			(
			[In, Out] SSCS_SECRET_STORE_ID_T	ssid,
			uint					 			ssFlags, 
			[In, Out] SSCS_EXT_T				ext
			);

		
		[DllImport(NDK_LIBRARY)]
		public static extern int miCASACloseSecretStoreCache 
			(
			[In]		IntPtr				context,
			[In]		uint				ssFlags, 
			[In, Out]	SSCS_EXT_T			ext
			);


		[DllImport(NDK_LIBRARY) ]
		public static extern int miCASAReadSecret
			(							
			[In]				IntPtr				context,
			[In]				SSCS_KEYCHAIN_ID_T	keyChainID, 
			[In]				uint				ssFlags, 
			[In]				IntPtr				secretHandle, 
			[In]				SSCS_SH_SECRET_ID_T	sharedSecretID, 
			[In]				SSCS_PASSWORD_T		epPassword, 
			[In, Out]			SSCS_READEXT_T		readData, 
			[In, Out]			SSCS_EXT_T			ext
			);


		// TODO: check the order of these paramters
		[DllImport(NDK_LIBRARY)]
		public static extern int miCASARemoveSecret
			(
			[In]		IntPtr					context,
			[In]		SSCS_KEYCHAIN_ID_T		keyChainID, 
			[In]		uint					ssFlags, 			
			[In]		SSCS_SH_SECRET_ID_T		sharedSecretID, 
			[In]		SSCS_PASSWORD_T			epPassword, 
			[In, Out]	SSCS_EXT_T				ext
			);


		[DllImport(NDK_LIBRARY)]
		public static extern int miCASAWriteSecret
			(
			[In]		IntPtr					context,
			[In]		SSCS_KEYCHAIN_ID_T		keyChainID, 
			[In]		uint					ssFlags, 
			[In]		IntPtr					secretHandle, 
			[In]		SSCS_SH_SECRET_ID_T		sharedSecretID, 
			[In]		SSCS_PASSWORD_T			epPassword, 
			[In]		SSCS_EXT_T				ext
			);

		[DllImport(NDK_LIBRARY)]
		public static extern int miCASAGetStoreInformation 
			(
			[In]		IntPtr					context,
			[In]		uint					ssFlags, 
			[In, Out]	SSCS_SECRETSTORE_INFO_T	ssInfo,
			[In, Out]	SSCS_EXT_T				ext
			);

		[DllImport(NDK_LIBRARY)]
		public static extern int miCASAEnumerateSecretIDs 
			(
			[In]		IntPtr						context,			
			[In]		SSCS_KEYCHAIN_ID_T			keyChainID, 
			[In]		uint						ssFlags, 
			[In]		SSCS_SRCH_KEY_T				searchKey, 
			[In, Out]	SSCS_SH_SECRET_ID_LIST_T	secretIDList, 
			[In, Out]	SSCS_EXT_T					ext
			);

		[DllImport(NDK_LIBRARY)]
		public static extern int miCASAEnumerateKeyChainIDs 
			(
			[In]		IntPtr					context,
			[In]		uint					ssFlags, 			
			[In]		SSCS_SRCH_KEY_T			searchKey, 
			[In, Out]	SSCS_SECRET_ID_LIST_T	kcIDList, 
			[In, Out]	SSCS_EXT_T				ext
			);

		[DllImport(NDK_LIBRARY)]
		public static extern int miCASARemoveSecretStore 
			(
			[In]		IntPtr					context,
			[In]		uint					ssFlags, 
			[In, Out]	SSCS_EXT_T				ext
			);
		
		[DllImport(NDK_LIBRARY)]
		public static extern int miCASASetMasterPassword 
			(
			[In]		uint					ssFlags, 
			[In]		SSCS_PASSWORD_T			password, 
			[In]		SSCS_HINT_T				hint, 
			[In, Out]	SSCS_EXT_T				ext
			);

		[DllImport(NDK_LIBRARY)]
		public static extern int miCASAUnlockSecrets 
			(
			[In]		IntPtr					context,
			[In]		uint					ssFlags, 
			[In]		SSCS_PASSWORD_T			password, 
			[In, Out]	SSCS_EXT_T				ext
			);

		[DllImport(NDK_LIBRARY)]
		public static extern int miCASALockStore
			(
			[In]		IntPtr					context,
			[In]		uint					ssFlags, 
			[In, Out]	SSCS_EXT_T				ext
			);

		[DllImport(NDK_LIBRARY)]
		public static extern int miCASAUnlockStore
			(
			[In]		IntPtr					context,
			[In]		uint					ssFlags, 
			[In]		SSCS_PASSWORD_T			password, 
			[In, Out]	SSCS_EXT_T				ext
			);


		[DllImport(NDK_LIBRARY)]
		public static extern int miCASARemoveKeyChain 
			(
			[In]		IntPtr					context,
			[In]		uint					ssFlags,
			[In]		SSCS_KEYCHAIN_ID_T		keyChainID, 
			[In, Out]	SSCS_EXT_T				ext
			);

		[DllImport(NDK_LIBRARY)]
		public static extern int miCASAAddKeyChain 
			(
			[In]		IntPtr					context,
			[In]		uint					ssFlags,
			[In]		SSCS_KEYCHAIN_ID_T		keyChainID, 
			[In, Out]	SSCS_EXT_T				ext
			);

		//**************************************************************
		//* Support Functions for processing (populating or extracting) 
		//* data components from a Shared Secret
		//**************************************************************
		[DllImport(NDK_LIBRARY)]
		public static extern IntPtr miCASA_CreateSHSHandle();
		
		[DllImport(NDK_LIBRARY)]
		public static extern int miCASA_DestroySHSHandle(
			[In] IntPtr secretHandle);
			

		[DllImport(NDK_LIBRARY, CharSet=CharSet.None) ]
		public static extern int miCASA_GetNextSHSEntry
			(
			[In]		int 			restart,		//* in (set to 1 to begin from head of list)
			[In]		IntPtr  		secretHandle,	//* in 
			[In, Out]	ref uint		keyLen,			//* out	
			IntPtr		theKey,
			[In, Out]	ref uint 		valLen,			//* out				
			IntPtr		theValue
			);
		

		[DllImport(NDK_LIBRARY, CharSet=CharSet.None)]
		public static extern int miCASA_AddSHSEntry
			(
			[In]		IntPtr  		secretHandle,	//* in]
			[MarshalAs(UnmanagedType.LPStr)]
			string			key,						//* in  /wchar
			[In]		uint			valueLenBytes,	// in bytes
			[MarshalAs(UnmanagedType.LPStr)]
			string			val							//* in /uchar			
			);

		[DllImport(NDK_LIBRARY, CharSet=CharSet.None)]
		public static extern int miCASAWriteKey
			(
			[In]		IntPtr					context,
			[In]		uint					ssFlags,
			[In]		SSCS_KEYCHAIN_ID_T		keyChainID, 
			[In]		SSCS_SH_SECRET_ID_T		sharedSecretID, 
			[MarshalAs(UnmanagedType.LPStr)]
			string					key,				//* in  /wchar
			[In]		uint					keyLenBytes,		// in bytes
			[MarshalAs(UnmanagedType.LPStr)]
			string					val,				//* in /uchar			
			[In]		uint					valueLenBytes,	// in bytes
			[In]		SSCS_PASSWORD_T			password, 
			[In, Out]	SSCS_EXT_T				ext				
			);
		
		[DllImport(NDK_LIBRARY)]
		public static extern int miCASA_RemoveSHSEntry
			(
			[In]		IntPtr  		secretHandle,	//* in 
			[MarshalAs(UnmanagedType.LPStr)]
			string			key,						//* out  /uchar
			uint			valueLenBytes,				// in bytes
			[MarshalAs(UnmanagedType.LPStr)]
			string			val							//* out /uchar
			);



		// Methods start here
		internal IntPtr openSecretStore(string sSecretStoreName)
		{			

			//check params
			if (sSecretStoreName == null || sSecretStoreName.Length==0)
				throw new miCasaException(miCasaException.NSSCS_E_INVALID_PARAM);
								

			SSCS_SECRET_STORE_ID_T SSid = new SSCS_SECRET_STORE_ID_T();			
			SSid.id = sSecretStoreName;
			SSid.version = 0; 

			SSCS_EXT_T ext = new SSCS_EXT_T();
			try
			{
				IntPtr hSC = miCASAOpenSecretStoreCache( SSid, 0, ext);
				return hSC;
			}
			catch (Exception)
			{
				return IntPtr.Zero;
			}
		}

		internal void closeSecretStore(IntPtr hSC)
		{
			SSCS_EXT_T ext = new SSCS_EXT_T();
			miCASACloseSecretStoreCache(hSC, 0, ext);
		}

		internal Secret getSecret(
			IntPtr pHsc,
			string sKeyChainID,
			uint   iFlags,
			string sSharedSecretID,
			int	iSecretType,
			string sEPPassword)						
		{					
	
			if (pHsc == IntPtr.Zero || sKeyChainID == null || sSharedSecretID == null
				|| sKeyChainID.Length == 0 || sSharedSecretID.Length == 0)
				throw new miCasaException(miCasaException.NSSCS_E_INVALID_PARAM);

			// setup structures
			SSCS_KEYCHAIN_ID_T		keyChainID = new SSCS_KEYCHAIN_ID_T();
			keyChainID.keychainID = sKeyChainID;

			SSCS_SH_SECRET_ID_T		sharedSecretID = new SSCS_SH_SECRET_ID_T();
			sharedSecretID.name = sSharedSecretID;
			
			SSCS_PASSWORD_T			epPassword = new SSCS_PASSWORD_T();
			SSCS_READEXT_T			readData = new SSCS_READEXT_T();
			SSCS_EXT_T				ext	=new SSCS_EXT_T();

			// get SecretHandle
			IntPtr pSecretHandle	= miCASA_CreateSHSHandle();
			
			// setup keychainid
			keyChainID.len = sKeyChainID.Length + 1;
			
			// setup sharedSecretID			
			sharedSecretID.len = sSharedSecretID.Length + 1;
			sharedSecretID.type = iSecretType;   // TODO: type APP(1), CRED(2), or Binary(4)
	
			epPassword.pwordType = 1;			// TODO: ep or master password based on flags
			if (sEPPassword != null)
			{
				epPassword.pword = sEPPassword;
				epPassword.pwordLen = (uint)sEPPassword.Length;
			}
			else
				epPassword.pwordLen = 0;
											
			int rcode = miCASAReadSecret(
				pHsc,
				keyChainID,
				iFlags,				
				pSecretHandle,
				sharedSecretID,
				epPassword,
				readData,
				ext);
			
			
			// create new secret
			Secret temp = new Secret(pHsc, sKeyChainID, iFlags, pSecretHandle, sSharedSecretID, sharedSecretID.type, sEPPassword);
			temp.setTimeStamps(readData.crtStamp, readData.lmtStamp, readData.latStamp);
			

			// if read was sucessful, read key value pairs
			if (rcode == 0)
				loadKeyValuePairs(temp);
			//			else
			//				throw new Exception();

			return temp;
		}

		internal void loadKeyValuePairs(Secret secret)
		{
			int rcode=0;
			int iStart = 1;
			IntPtr theKey = Marshal.AllocHGlobal(60416);
			IntPtr theValue = Marshal.AllocHGlobal(60416);
			String sKey;
			String sValue;

			while (true)
			{			
				uint iKeyLen = 60416;						
				uint iValueLen = 60416;

				sKey = "";
				sValue = "";
				
				rcode = miCASA_GetNextSHSEntry(
					iStart,
					secret.m_secretHandle,
					ref iKeyLen,
					theKey, //sKey,
					ref iValueLen,
					theValue // sValue
					);


				if (rcode != 0) 
					break;
				else
				{                          
					if (iKeyLen > 1)
					{
						//sKey = Marshal.PtrToStringAnsi(theKey, (int)iKeyLen - 1);

						// convert from UTF8
						byte[] baKey = new byte[iKeyLen-1];
						Marshal.Copy(theKey, baKey, 0, (int)iKeyLen-1);
						sKey = System.Text.Encoding.UTF8.GetString(baKey, 0, baKey.Length);
					}
					
					if (iValueLen > 1)
					{
						//sValue = Marshal.PtrToStringAnsi(theValue, (int)iValueLen -1);					

						// convert from UTF8
						byte[] baValue = new byte[iValueLen-1];
						Marshal.Copy(theValue, baValue, 0, (int)iValueLen-1);
						sValue = System.Text.Encoding.UTF8.GetString(baValue, 0, baValue.Length);
					}
					
					if (sKey != null && sValue != null)				
						secret.setKeyValuePair(sKey, sValue);

						
					iStart = 0;
				}
			
			}	
			Marshal.FreeHGlobal(theKey);
			Marshal.FreeHGlobal(theValue);		
		}

		internal static int removeKeyValue(
			IntPtr pSecretHandle, 
			string sKey, 
			string sValue,	
			uint ctxFlags)
		{

			if ((sKey == null) || (sValue == null))
				return 0;

			int rcode = miCASA_RemoveSHSEntry(
				pSecretHandle,
				sKey,
				(uint)(sValue.Length + 1) * 2,
				sValue);

			return rcode;
		}

		/*
				internal int setSecret(
					IntPtr pHsc,    
					string sKeyChainID, 
					uint   iFlags, 	
					Secret secret, 
					int		iSecretType)
				{			
					int rcode;


					if (iSecretType == Secret.SS_BINARY)
					{
						rcode = miCASA_AddSHSEntry(
							secret.m_secretHandle,
							"SS_Binary",
							(uint)secret.getBinaryValue().Length,
							secret.getBinaryValue().ToString());				
					}
					else
					{
						NameValueCollection nvc = secret.getKeyValueCollection();			
						if (nvc != null)
						{
							for (int i=0; i<nvc.Count; i++)
							{

								String sKey = nvc.GetKey(i);
								String sValue = nvc.Get(sKey);			// = new StringBuilder();								

								if (sKey.Length > 0) 
								{
									// first write out all key/value pairs				
									rcode = miCASA_AddSHSEntry(
										secret.m_secretHandle,
										sKey,							//sKey,		
										(uint)(sValue.Length+1) * 2,
										sValue);						//sValue);  						
								}

							}
						}
					}

					// now call miCASAWriteSecret
					SSCS_KEYCHAIN_ID_T		keyChainID = new SSCS_KEYCHAIN_ID_T();
					keyChainID.keychainID = sKeyChainID;

					SSCS_SH_SECRET_ID_T		sharedSecretID = new SSCS_SH_SECRET_ID_T();
					SSCS_PASSWORD_T			epPassword = new SSCS_PASSWORD_T();			
					SSCS_EXT_T				ext	=new SSCS_EXT_T();
					ext.extID = 0;
					ext.version = 0;
			

					// setup keychainid
					keyChainID.len = sKeyChainID.Length + 1;

					String tempStr = secret.getID();
					sharedSecretID.name = tempStr;
					sharedSecretID.len = tempStr.Length + 1;	
					sharedSecretID.type = iSecretType;				// TODO: type APP(1), CRED(2), or Binary(4)

					if (secret.getEnhancedProtectionPassword() != null)
					{
						epPassword.pword = secret.getEnhancedProtectionPassword();
						epPassword.pwordLen = (uint)secret.getEnhancedProtectionPassword().Length + 1;
						epPassword.pwordType = 0;
					}
					else
					{
						epPassword = null;
					}
			
					ext.ext = Marshal.AllocHGlobal(10);
					try 
					{
						rcode = miCASAWriteSecret(
							pHsc,
							keyChainID,
							iFlags,
							secret.m_secretHandle,
							sharedSecretID,
							epPassword,
							ext);		
					}
					catch (Exception)
					{
						//Console.WriteLine(e.ToString());
						rcode = -803;
					}
		
					Marshal.FreeHGlobal(ext.ext);
					return rcode;
				}
		*/

		internal bool KeyInNewList(NameValueCollection nvc, string key)
		{

			//Console.WriteLine("Checking for " + key );
			if (nvc != null)
			{
				for (int i = 0; i < nvc.Count; i++)
				{
					string sKey = nvc.GetKey(i);
					if( key == sKey )
						return true;
				}
			}
			//Console.WriteLine("key = " + key + " is not present in nvc");
			return false;
		} 


		internal int setSecret(
			IntPtr pHsc,    
			string sKeyChainID, 
			uint   iFlags, 	
			Secret secret, 
			int		iSecretType)
		{			

			int rcode = 0;
			if (sKeyChainID == null || sKeyChainID.Length == 0)
				throw new miCasaException(miCasaException.NSSCS_E_INVALID_PARAM);
			
			// now call miCASAWriteSecret
			SSCS_KEYCHAIN_ID_T		keyChainID = new SSCS_KEYCHAIN_ID_T();
			keyChainID.keychainID = sKeyChainID;

			SSCS_SH_SECRET_ID_T		sharedSecretID = new SSCS_SH_SECRET_ID_T();
			SSCS_PASSWORD_T			epPassword = new SSCS_PASSWORD_T();			
			SSCS_EXT_T				ext	=new SSCS_EXT_T();
			ext.extID = 0;
			ext.version = 0;
			

			// setup keychainid
			keyChainID.len = sKeyChainID.Length + 1;

			String tempStr = secret.getID();
			sharedSecretID.name = tempStr;
			sharedSecretID.len = (tempStr.Length + 1) * 2;	
			sharedSecretID.type = iSecretType;				// TODO: type APP(1), CRED(2), or Binary(4)

			if (secret.getEnhancedProtectionPassword() != null)
			{
				epPassword.pword = secret.getEnhancedProtectionPassword();
				epPassword.pwordLen = (uint)secret.getEnhancedProtectionPassword().Length + 1;
				epPassword.pwordType = 0;
			}
			else
			{
				epPassword = null;
			}
			
			ext.ext = Marshal.AllocHGlobal(10);


			if (iSecretType == Secret.SS_BINARY)
			{
				rcode = miCASA_AddSHSEntry(
					secret.m_secretHandle,
					"SS_Binary",
					(uint)secret.getBinaryValue().Length,
					secret.getBinaryValue().ToString());				
			}
			else
			{
				NameValueCollection nvc = secret.getKeyValueCollection();			
				if (nvc != null)
				{
					for (int i=0; i<nvc.Count; i++)
					{

						rcode = 0;
						String sKey = nvc.GetKey(i);
						String sValue = nvc.Get(sKey);			// = new StringBuilder();								

						if (sKey.Length > 0) 
						{
							try 
							{																
								object o = MiCasaRequestReply.Send(MiCasaRequestReply.VERB_WRITE_KEY,
									sKeyChainID,
									secret.getID(),
									sKey,
									EscapeReservedChars(sValue));

								/*
								rcode = miCASAWriteKey(
									pHsc,
									iFlags,
									keyChainID,
									sharedSecretID,
									sKey,
									(uint) (sKey.Length+1) * 2,
									sValue,
									(uint) (sValue.Length+1) * 2,
									epPassword,
									ext);
								*/
							}
							catch (Exception e)
							{								
								rcode = -803;
							}
						}
					}

					if( 0 == rcode )
					{
						try
						{

							ArrayList keyList = (ArrayList)MiCasaRequestReply.Send(MiCasaRequestReply.VERB_GET_KEY_LIST,sKeyChainID,secret.getID(),null,null);

							if( null != keyList )
							{
								IEnumerator etor = keyList.GetEnumerator();
								while( etor.MoveNext() )
								{
									string key = (string)etor.Current;
									if(KeyInNewList(nvc,key) == false)
									{
										MiCasaRequestReply.Send(MiCasaRequestReply.VERB_REMOVE_KEY,sKeyChainID,secret.getID(),key, null );           
									}
                                                    
								}
							}
						}
						catch(Exception e)
						{
							rcode = -803;
							//Console.WriteLine(e.ToString());
						}
					}
				}
			}

			Marshal.FreeHGlobal(ext.ext);
			return rcode;
		}


		internal int RemoveSecret(
			IntPtr context,
			uint ssFlags,
			string sKeyChainID,
			string sEPPassword,
			string sSecretID,
			int iSecretType)
		{
			
			if (context == IntPtr.Zero || sKeyChainID == null || sSecretID == null
				|| sKeyChainID.Length==0 || sSecretID.Length==0)
				return miCasaException.NSSCS_E_INVALID_PARAM;
			
			int rcode = 0;

			SSCS_KEYCHAIN_ID_T keyChainID = new SSCS_KEYCHAIN_ID_T();
			keyChainID.keychainID = sKeyChainID;
			keyChainID.len = sKeyChainID.Length + 1;

			SSCS_PASSWORD_T epPassword = new SSCS_PASSWORD_T();
			epPassword.pword = sEPPassword;
			epPassword.pwordLen = (uint)sEPPassword.Length + 1;
			epPassword.pwordType = 0;  // todo set type

			SSCS_SH_SECRET_ID_T secretID = new SSCS_SH_SECRET_ID_T();
			secretID.name = sSecretID;

			secretID.len = sSecretID.Length + 1;
			secretID.type = iSecretType;

			SSCS_EXT_T ext = new SSCS_EXT_T();

			// make the call
			rcode = miCASARemoveSecret(
				context,
				keyChainID,
				ssFlags,				
				secretID,
				epPassword,
				ext);
			

			return rcode;

		}

		internal StringCollection EnumerateSecretIDs(
			IntPtr context,
			uint ssFlags,
			string sKeyChainID,
			string sSearchKey)
		{


			if (context == IntPtr.Zero || sKeyChainID==null || sSearchKey==null
				|| sKeyChainID.Length==0)

				throw new miCasaException(miCasaException.NSSCS_E_INVALID_PARAM);

			// setup structures
			SSCS_KEYCHAIN_ID_T keyChainID		= new SSCS_KEYCHAIN_ID_T();
			keyChainID.keychainID = sKeyChainID;

			SSCS_SRCH_KEY_T	searchKey			= new SSCS_SRCH_KEY_T();
			SSCS_SH_SECRET_ID_LIST_T  idList	= new SSCS_SH_SECRET_ID_LIST_T();
			SSCS_EXT_T ext						= new SSCS_EXT_T();

			int numIDS = 1024;
			StringCollection sc = new StringCollection(); //null;

			if (USE_OBJECT_SERIALIZATION)
			{
				sc = (StringCollection)MiCasaRequestReply.Send(MiCasaRequestReply.VERB_GET_SECRETIDS, null, null, null, sc);
				return sc;	
			}
			else
			{

				// setup keychainid
				keyChainID.len = sKeyChainID.Length + 1;

				searchKey.srchKey = sSearchKey;
				searchKey.srchKeyLen = (uint)sSearchKey.Length;
						
				idList.returnedIDs = (uint)numIDS;

				SSCS_SH_SECRET_ID_T secretId = new SSCS_SH_SECRET_ID_T();
				//			secretId.name = new char[512];
			
				StringBuilder buffer3 = new StringBuilder( "content", 512 ); 
				buffer3.Append( (char)0 );
				buffer3.Append( '*', buffer3.Capacity - 8 );
				String tempStr = buffer3.ToString();
				idList.secretIDList = Marshal.AllocHGlobal(numIDS * Marshal.SizeOf(secretId));
			
						
				// make the call
				int rcode = miCASAEnumerateSecretIDs(
					context,				
					keyChainID,
					ssFlags,
					searchKey,
					idList,
					ext);

				if (idList.returnedIDs > 0)
				{					
					sc = new StringCollection();
				
					if (idList.returnedIDs != numIDS)
					{
						// enumerate returned list
						for (int i=0; i<idList.returnedIDs; i++)
						{			
							IntPtr temp = new IntPtr(idList.secretIDList.ToInt32() + (i * Marshal.SizeOf(secretId)));
							secretId = (SSCS_SH_SECRET_ID_T)Marshal.PtrToStructure(temp, typeof(SSCS_SH_SECRET_ID_T));

							//						String st = new String(secretId.name,0,secretId.len - 1);
							String st = secretId.name;
							sc.Add(st);
						}
					}			
				}			

				Marshal.FreeHGlobal(idList.secretIDList);
				return sc;
			}
		}

		public void AddKeyChain(
			IntPtr context,
			uint ssFlags,
			string sKeyChainID)
		{

			if (context==IntPtr.Zero || sKeyChainID==null || sKeyChainID.Length==0)
				throw new miCasaException(miCasaException.NSSCS_E_INVALID_PARAM);

			SSCS_KEYCHAIN_ID_T keyChainID		= new SSCS_KEYCHAIN_ID_T();
			keyChainID.keychainID				= sKeyChainID;
			keyChainID.len = sKeyChainID.Length + 1;
			
			SSCS_EXT_T ext						= new SSCS_EXT_T();
			int rcode = miCASAAddKeyChain(context, ssFlags, keyChainID, ext);
		}


		public static BasicCredential GetCredential(
			uint ssFlags,
			string sAppSecretID,
			string sSharedSecretID,
			uint unFlag)

		{
			if (sAppSecretID==null || sAppSecretID.Length == 0)
				throw new miCasaException(miCasaException.NSSCS_E_INVALID_PARAM);

			int rcode;
			uint credType = 0;
			BasicCredential bc = null;

			SSCS_SECRET_ID_T appSecretID = new SSCS_SECRET_ID_T();
			appSecretID.id = sAppSecretID;
			appSecretID.len = sAppSecretID.Length + 1;

			SSCS_SECRET_ID_T sharedID = new SSCS_SECRET_ID_T();
			if (sSharedSecretID != null)
			{
				sharedID.len = sSharedSecretID.Length + 1;
				sharedID.id = sSharedSecretID;

			}

			SSCS_BASIC_CREDENTIAL_UTF8 credential = new SSCS_BASIC_CREDENTIAL_UTF8();
			credential.unFlags = unFlag;
			
			// alloc some memory
			IntPtr pCredential = Marshal.AllocHGlobal(Marshal.SizeOf(credential));			
			Marshal.StructureToPtr(credential, pCredential, false);
			
			try 
			{
				rcode = miCASAGetCredential(
					ssFlags,
					appSecretID,
					sharedID,
					ref credType,
					pCredential,
					new SSCS_EXT_T()
					);
			}
			catch (Exception e)
			{
				//Console.WriteLine(e.ToString());
				Marshal.FreeHGlobal(pCredential);
				throw new miCasaException(-803);
			}

			if (rcode == 0)
			{
				Marshal.PtrToStructure(pCredential, credential);
				string sUsername = GetStringFromUTF8(credential.username, (int)credential.unLen - 1);
				string sPassword = GetStringFromUTF8(credential.password, (int)credential.pwordLen - 1);
				
				bc = new BasicCredential(sUsername, sPassword);
				Marshal.FreeHGlobal(pCredential);
				return bc;
			}
			else
			{
				Marshal.FreeHGlobal(pCredential);
				throw new miCasaException(rcode);
			}
						
		}


		public static void SetCredential(
			uint ssFlags,
			string sAppSecretID,
			string sSharedSecretID,
			uint unFlag,
			string sUsername,
			string sPassword)
		{

			if (sAppSecretID==null || sUsername==null || sPassword==null
				|| sAppSecretID.Length==0 || sUsername.Length==0 || sPassword.Length==0)
				throw new miCasaException(miCasaException.NSSCS_E_INVALID_PARAM);

			int rcode;
			
			SSCS_SECRET_ID_T appSecretID = new SSCS_SECRET_ID_T();
			appSecretID.id = sAppSecretID;
			appSecretID.len = sAppSecretID.Length + 1;

			SSCS_SECRET_ID_T sharedID = new SSCS_SECRET_ID_T();
			if (sSharedSecretID != null)
			{
				sharedID.len = sSharedSecretID.Length + 1;
				sharedID.id = sSharedSecretID;
			}

			SSCS_BASIC_CREDENTIAL_UTF8 credential = new SSCS_BASIC_CREDENTIAL_UTF8();
			credential.unFlags = unFlag;									

			credential.unLen = GetUTF8ByteCount(sUsername) + 1;
			credential.username = GetUTF8FromString(sUsername, USERNAME_LEN);

			credential.pwordLen = GetUTF8ByteCount(sPassword) + 1;
			credential.password = GetUTF8FromString(sPassword, PASSWORD_LEN);

			IntPtr pCredential = Marshal.AllocHGlobal(Marshal.SizeOf(credential));

			Marshal.StructureToPtr(credential, pCredential, true);
				
			rcode = miCASASetCredential
				(ssFlags,
				appSecretID,
				sharedID,
				SSCS_CRED_TYPE_BASIC_F,
				pCredential,
				new SSCS_EXT_T()
				);

			if (rcode != 0)
			{
				throw new miCasaException(rcode);
			}
		}
	

		public static void RemoveCredential(
			uint ssFlags,
			string sAppSecretID,
			string sSharedSecretID)
	
		{
			if (sAppSecretID == null || sAppSecretID.Length==0)
				throw new miCasaException(miCasaException.NSSCS_E_INVALID_PARAM);

			int rcode;			
			SSCS_SECRET_ID_T appSecretID = new SSCS_SECRET_ID_T();
			appSecretID.id = sAppSecretID;
			appSecretID.len = sAppSecretID.Length + 1;

			SSCS_SECRET_ID_T sharedID = new SSCS_SECRET_ID_T();
			if (sSharedSecretID != null)
			{
				sharedID.len = sSharedSecretID.Length + 1;
				sharedID.id = sSharedSecretID;
			}
						
			rcode = miCASARemoveCredential(ssFlags, appSecretID, sharedID, new SSCS_EXT_T());

			if (rcode != 0)
			{
				throw new miCasaException(rcode);
			}
			
		}

		internal static int SetMasterPasscode(
			uint ssFlags,
			string passcodeStr)
		{

			if (passcodeStr==null || passcodeStr.Length==0)
				throw new miCasaException(miCasaException.NSSCS_E_INVALID_PARAM);

			int rcode = -1;			
			try
			{
				SSCS_PASSCODE_T passcode = new SSCS_PASSCODE_T();
				SSCS_PASSWORD_T passwd = new SSCS_PASSWORD_T();

				passwd.pwordType = 1;
				/* UTF8 ??
										passwd.pword = GetUTF8FromString(passcodeStr,PASSWORD_LEN);
										passwd.pwordLen = GetUTF8ByteCount(passcodeStr);
				*/
				passwd.pword = passcodeStr;
				passwd.pwordLen = (uint)passcodeStr.Length;


				passcode.passcodeType = 1;
				IntPtr temp = Marshal.AllocHGlobal(Marshal.SizeOf(passwd));
				Marshal.StructureToPtr(passwd, temp, false);
				passcode.passcodeHandle = temp;
 
				rcode = miCASASetMasterPasscode(ssFlags, 
					passcode, 
					new SSCS_EXT_T());
			}
			catch(Exception e)
			{
				//Console.WriteLine(e.ToString());
			}
			return rcode;
		}
		public static int SetMasterPassword(
			uint ssFlags,
			string mPasswd)
		{

			if (mPasswd==null || mPasswd.Length==0)
				return 1;

			int rcode = -1;			
			try
			{
				SSCS_PASSWORD_T passwd = new SSCS_PASSWORD_T();

				passwd.pwordType = 1;
				passwd.pword = mPasswd;
				passwd.pwordLen = (uint)mPasswd.Length;

 
				rcode = miCASASetMasterPassword(ssFlags, 
					passwd, 
					null,
					new SSCS_EXT_T());
			}
			catch(Exception e)
			{
				//Console.WriteLine(e.ToString());
			}
			return rcode;
		}

		public static bool IsSecretPersistent(uint ssFlags, string id)
		{
			int rcode = 0;
						
			SSCS_SECRET_ID_T secretID = new SSCS_SECRET_ID_T();
			try 
			{
				if(ssFlags == 0)
				{
					if ((null == id) || ("" == id))
			            return false;

					secretID.len = id.Length;
					secretID.id = id;
					rcode = miCASAIsSecretPersistent(ssFlags,
						secretID,
						new SSCS_EXT_T());
				}
				else
				{
					rcode = miCASAIsSecretPersistent(ssFlags,
						null,
						new SSCS_EXT_T());
				}
			}
			catch (Exception e)
			{
				//Console.WriteLine(e.ToString());
			}
			
			
			if(rcode == 1)
				return true;
			else
				return false;
		}

			
		private static string GetStringFromUTF8(byte[] utf8Bytes, int numBytes)
		{
			if (numBytes > 0)
			{
				Decoder utfDec = System.Text.Encoding.UTF8.GetDecoder();
				int charCount = utfDec.GetCharCount(utf8Bytes, 0, numBytes);
				char[] chars = new char[charCount];
				int len = utfDec.GetChars(utf8Bytes, 0, numBytes, chars, 0);

				string text = new string(chars);
				text = text.Trim();
				return text;
			}
			else
				return "";
		}

		private static uint GetUTF8ByteCount(string str)
		{
			uint byteCount = (uint)System.Text.Encoding.UTF8.GetEncoder().GetByteCount(str.ToCharArray(), 0, str.Length, true);
			return byteCount;
		}

		private static byte[] GetUTF8FromString(string str, int arrayLength)
		{
			// NOTE: Must return a padded array of arrayLength
			Encoder utfEnc = System.Text.Encoding.UTF8.GetEncoder();						
			byte[] utf8Bytes = new byte[arrayLength];
			int len = utfEnc.GetBytes(str.ToCharArray(), 0, str.Length, utf8Bytes, 0, true);
			return utf8Bytes;
		}
		internal int RemoveKey(
			string sKeyChainID, 
			string secretID,
			string keyID)
		{		
			if (sKeyChainID==null || secretID==null || keyID==null 
				|| sKeyChainID.Length==0 || secretID.Length==0 || keyID.Length==0)
				throw new miCasaException(miCasaException.NSSCS_E_INVALID_PARAM);

			int rcode = 0;
			
			if (keyID.Length > 0) 
			{
				try 
				{																
					object o = MiCasaRequestReply.Send(
						MiCasaRequestReply.VERB_REMOVE_KEY,
						sKeyChainID, secretID, keyID, null);
				}
				catch (Exception e)
				{
					rcode = -803;
				}
			}
			return rcode;
		}

		internal ArrayList GetKeyList(
			string sKeyChainID,
			string secretID)
		{
			if (sKeyChainID==null || secretID==null
				|| sKeyChainID.Length==0 || secretID.Length==0)
				throw new miCasaException(miCasaException.NSSCS_E_INVALID_PARAM);

			ArrayList keyList = null;
			try
			{
				keyList = (ArrayList) MiCasaRequestReply.Send(
					MiCasaRequestReply.VERB_GET_KEY_LIST,
					sKeyChainID, secretID,null,null);
				return keyList;
#if false

                        if( null != keyList )
                        {
                            IEnumerator etor = keyList.GetEnumerator();
                            while( etor.MoveNext() )
                            {
                                Console.WriteLine("Key = " + etor.Current);
                            }
                        }
#endif

			}
			catch(Exception e)
			{
				//Console.WriteLine(e.ToString());
				keyList = null;
			}
			return keyList;
		}                


		internal string ReadKey(
			string sKeyChainID, 
			string secretID,
			string keyID)
		{			
			if (sKeyChainID==null || secretID==null || keyID==null
				|| sKeyChainID.Length==0 || secretID.Length==0 || keyID.Length==0)
				throw new miCasaException(miCasaException.NSSCS_E_INVALID_PARAM);

			int rcode = 0;
			string value = null;			

			if (keyID.Length > 0) 
			{
				try 
				{																
					value = (String)MiCasaRequestReply.Send(
						MiCasaRequestReply.VERB_REMOVE_KEY,
						sKeyChainID, secretID, keyID, null);
				}
				catch (Exception e)
				{
					//			        rcode = -803;
					//Console.WriteLine(e.ToString());
				}
			}
			return value;
		}

		private string EscapeReservedChars(string origString)
		{
			if (origString==null)
				return origString;

			StringBuilder sb = new StringBuilder();
			for (int i=0; i<origString.Length; i++)
			{
				switch (origString[i])
				{
					case ':'  :	
					{
						sb.Append("\\");
						break;
					}
					case '\\' :		
					{
						sb.Append("\\");
						break;
					}
					case '='  :
					{
						sb.Append("\\");
						break;
					}					
					
				}
				sb.Append(origString[i]);				
			}
			return sb.ToString();
		}

		internal static bool ResetMasterPassword(string sCurrentPassword, string sNewPassword)
		{
			ResetMasterPassword rmp = new ResetMasterPassword(sCurrentPassword, sNewPassword);
			ResetMasterPassword o = (ResetMasterPassword)MiCasa.Communication.MiCasaRequestReply.Send(
				MiCasaRequestReply.VERB_RESET_MASTER_PASSWORD,
				rmp);

			if (o.rcode != 0)
				return false;
			else 
				return true;
		}

		internal static bool ValidateDesktopPwd(string sPassword)
		{			
			string sResult = (string)MiCasa.Communication.MiCasaRequestReply.Send(MiCasaRequestReply.VERB_VALIDATE_DESKTOP_PWD, null, null, null, sPassword);

			if (sResult.Equals("true"))
				return true;
			else
				return false;
							
		}
	}
}