using System;
using System.Text;
using System.Runtime.InteropServices;

namespace Novell.SecretStore.NSSSWrapper
{
	/// <summary>
	/// Summary description for Class1.
	/// </summary>
	public class NativeCalls
	{
		//private uint NSSS_GET_CONTEXT_F	= 0x00000100;
		private string m_host = null;
		private string m_userId = null;
		private string m_password = null;
		private string m_certFile = null;

		private SSS_CONTEXT_T m_context = null;
		private SS_OBJECT_DN_T m_objectDN = null;
	
		public NativeCalls(string host, string userId, string password, string certFile)
		{
			//
			// TODO: Add constructor logic here
			//
			m_host = host;
			m_userId = userId;
			m_password = password;
			m_certFile = certFile;
		}

		//* Get service info extended data 
		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
			public class SSS_GSINFOEXT_T
		{
			public uint statFlags;
			public uint secretCount;
			public uint lockCount;
			public uint enumBufLen;
			public uint hidSecCount;
			public uint clientVersion;
			public uint serverVersion;
			public uint serverCryptoStrength;
			public uint clientCryptoStrength;
			public uint unlockTStamp;
			public uint admnDNLen;
			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 516)]
			public string admnDN; //[NSSS_MAX_DN_LEN];
			public uint hintLen;
			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
			public string hint; //[NSSS_MAX_MP_PWORD_HINT_LEN]];
		}

		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
			public class SS_SERVER_INFO_T
		{
			//char				treeName[NSSS_MAX_TREE_NAME_LEN];			
			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)] // in bytes?
			public string	treeName;
			
			//char				ssServerDN[NSSS_MAX_DN_LEN];
			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 516)]
			public string	ssServerDN;

			//char				ssServerIPAddr[NSSS_MAX_IP_ADDR_LEN];
			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
			public string  ssServerIPAddr;

			//char				sssConfigDN[NSSS_MAX_DN_LEN];
			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 516)]
			public string sssConfigDN;
		} ;

		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
			public class SS_OBJECT_DN_T
		{
			public int	len;
			//char			id[NSSS_MAX_DN_LEN];
			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 516)]			
			public string id;
		};

		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
			public class SSS_CONTEXT_T
		{
			public		uint		flags;			//* IN- context type indicator and 			
			public		uint		dsCtx;			//* IN/OUT- ldap/ncp context
			public		uint		version;		//* IN- context version indicator
			public		SS_SERVER_INFO_T	ssServerInfo;	//* IN/OUT- preferred SecretStore server info
			public		SS_OBJECT_DN_T		callerDN; 		//* IN/OUT- DN of the caller. OUT-For NCP.
			public		IntPtr		handles;
			public		IntPtr		bindInfo;
		}

		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
			public class SSCS_EXT_T
		{
			public		int			extID; 		// defined to identify the extension 
			public		int			version;	// defined as the version of the specified extension
			//void		*ext;		// points to the actual extension
			public		IntPtr		ext;			
		} ;

		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
		public class SS_LDAPBIND_INFO_T
		{
			public	uint		portNum;								//* default: 636
			//char				lHostName[ NSSS_MAX_DN_LEN ]; 		//* Ex: "nsd10.novell.com" or ip addr
			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 516)]
				public string lHostHame;

			//char				trustedRootCert[ NSSS_MAX_DN_LEN ]; 	//* default: "c:\TrustedRootCertificate.der"
			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 516)]					
				public string trustedRootCert;

			//char				loginPword[ NSSS_MAX_LDAP_PWORD_LEN ];	//* LDAP login password (utf8)
			[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]					
				public string loginPword;
		} ;

		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
		public class SS_SECRET_T
		{
			public int				len;
			public IntPtr			data;
		};


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

		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
		public class SSS_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
		};

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

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

#if DEBUG	
		private const string NDK_LIBRARY = "nsss";	
		//private const string NDK_LIBRARY = "/opt/novell/sss/libnsss.so";	
#else 
		private const string NDK_LIBRARY = "nsss";
#endif    

		[DllImport(NDK_LIBRARY, CharSet=CharSet.Ansi)]
		public static extern int NSSSGetServiceInformation
			(
				[In, Out]	SSS_CONTEXT_T			context,		
				[In]		SS_OBJECT_DN_T			targetObjDN, 
				[In]		uint 					ssFlags,
				[In, Out]	SSS_GSINFOEXT_T			gsData,
				[In, Out]	SSCS_EXT_T				ext
			);

		[DllImport(NDK_LIBRARY, CharSet=CharSet.Ansi)]
		public static extern int NSSSEnumerateSecretIDs
			(
				[In, Out]	SSS_CONTEXT_T			context,		
				[In]		SS_OBJECT_DN_T			targetObjDN, 
				[In]		uint 					ssFlags,
				[In, Out]	IntPtr					srchStr, 
				[In, Out]   ref uint				count, 
				[In, Out]	SS_SECRET_T				secretIDList, 
				[In, Out]	SSCS_EXT_T				ext
			);

		[DllImport(NDK_LIBRARY, CharSet=CharSet.Ansi)]
		public static extern int NSSSReadSecret
			(
				[In, Out]	SSS_CONTEXT_T			context,		
				[In]		SS_OBJECT_DN_T			targetObjDN, 
				[In]		uint 					ssFlags,
				[In]		SS_PWORD_T				epPassword,
				[In, Out]	SSS_READEXT_T			readData,
				[In]		SS_SECRET_ID_T			secretID,
				[In, Out]	SS_SECRET_T 	 		secretValue,
				[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 NSSSCreateSHSHandle();

		[DllImport(NDK_LIBRARY)]
		public static extern int NSSSDestroySHSHandle(
			[In]	IntPtr		handle);	// in 


		[DllImport(NDK_LIBRARY, CharSet=CharSet.Ansi) ]
		public static extern int NSSSGetNextSHSEntry
			(
			[In]		int 					restart,		//* in (set to 1 to begin from head of list)
			[In]		IntPtr  				secretHandle,	//* in 
			[In, Out]	ref uint				keyLen,			//* out	
			//[MarshalAs(UnmanagedType.LPTStr)]
			//			StringBuilder			key,			//* out  uchar
			[In, Out]   IntPtr					pKey,
			[In, Out]	ref uint 				valLen,			//* out				
			//[MarshalAs(UnmanagedType.LPTStr)]
			//			StringBuilder			val,			//* out uchar
			[In, Out]   IntPtr					pVal,
			[In]		uint					ssCtxFlags
			);

		[DllImport(NDK_LIBRARY, CharSet=CharSet.Ansi)]
		public static extern int NSSSAddSHSEntry
			(
			[In]		IntPtr  				secretHandle,	//* in]
			//[MarshalAs(UnmanagedType.B)]
						string					key,			//* in  /wchar			
			//[MarshalAs(UnmanagedType.LPTStr)]
						string					val,			//* in /uchar			
			[In]		uint					ssCtxFlags
			);


		[DllImport(NDK_LIBRARY)]
		public static extern int NSSSRemoveSHSEntry
			(
			[In]		IntPtr  				secretHandle,	//* in 
			[MarshalAs(UnmanagedType.LPWStr)]
						string					key,			//* out  /uchar
			[MarshalAs(UnmanagedType.LPTStr)]
						string					val,			//* out /uchar
			[In]		uint					ssCtxFlags
			);


		//**************************************************************
		//* These function calls will utilize the Support Functions for 
		//* populating or extracting data from a Shared Secret.
		//**************************************************************
		
		[DllImport(NDK_LIBRARY)]
		public static extern int NSSSWriteSharedSecret
			(
				[In]		IntPtr					secretHandle,	//* in 
				[In]		SS_SH_SECRET_ID_T		pSharedSecret,
				[In, Out]	SSS_CONTEXT_T			context,		
				[In]		SS_OBJECT_DN_T			targetObjDN, 
				[In]		uint 					ssFlags,
				[In]		SS_PWORD_T				epPassword,
				[In, Out]	SSCS_EXT_T				ext
			);

		[DllImport(NDK_LIBRARY)]
		public static extern int NSSSReadSharedSecret
			(
				[In]		IntPtr					secretHandle,	//* in 
				[In]		SS_SH_SECRET_ID_T		pSharedSecret,
				[In, Out]	SSS_CONTEXT_T			context,		
				[In]		SS_OBJECT_DN_T			targetObjDN, 
				[In]		uint 					ssFlags,
				[In]		SS_PWORD_T				epPassword,
				[In, Out]	SSS_READEXT_T			readData,
				[In, Out]	SSCS_EXT_T				ext
			);

		[DllImport(NDK_LIBRARY)]
		public static extern int NSSSRemoveSharedSecret
			(
				[In]		SS_SH_SECRET_ID_T		pSharedSecret,
				[In, Out]	SSS_CONTEXT_T			context,		
				[In]		SS_OBJECT_DN_T			targetObjDN, 
				[In]		uint 					ssFlags,
				[In, Out]	SSCS_EXT_T				ext
			);


		// code starts here

		public void setContextInfo(string sHost, string sUsername, string sPassword, string sCertFile)
		{
			m_host = sHost;
			m_userId = sUsername;
			m_password = sPassword;
			m_certFile = sCertFile;
		}


		public void getStoreInfo()
		{
			// set up locals
			SSS_CONTEXT_T context = new SSS_CONTEXT_T();
			SS_OBJECT_DN_T objectDN = new SS_OBJECT_DN_T();
			SS_LDAPBIND_INFO_T ldapBindInfo = new SS_LDAPBIND_INFO_T();
			SSS_GSINFOEXT_T gsData = new SSS_GSINFOEXT_T();
			SSCS_EXT_T ext = new SSCS_EXT_T();

			// init params
			objectDN.id = m_userId;
			objectDN.len = objectDN.id.Length + 1;
			
			//ldapBindInfo.lHostHame = "151.155.152.209";
			//ldapBindInfo.lHostHame = "jim1.provo.novell.com";
			ldapBindInfo.lHostHame = m_host;
			//ldapBindInfo.loginPword = "test";
			ldapBindInfo.loginPword = m_password;
			ldapBindInfo.portNum = 636;
			ldapBindInfo.trustedRootCert = m_certFile;
			
			
			context.bindInfo = Marshal.AllocHGlobal(Marshal.SizeOf(ldapBindInfo));
			Marshal.StructureToPtr(ldapBindInfo, context.bindInfo, false);
			
			//context.callerDN = objectDN;
			context.callerDN = objectDN;
			context.flags = 2050;
			context.handles = IntPtr.Zero;
			context.version = 0;
			//context.bindInfo = ldapBindInfo;
			//context.bindInfo = new IntPtr(4);	
	
			context.ssServerInfo = new SS_SERVER_INFO_T();
			Console.WriteLine("ServerInfoSize: "+Marshal.SizeOf(context.ssServerInfo));
			

			int rcode = NSSSGetServiceInformation(context,
				objectDN,
				0x00000100, //0x00000110, //  0x00000010,  ALL STRINGS UNICODE.... 
				gsData,
				ext);

			if (rcode != 0) 
			{
				Console.Write("NSSSGetServiceInformation return error: " + rcode);				
				throw new Exception("Login Failed");
			}
			else
			{
				m_context = context;
				m_objectDN = objectDN;
			}
				
			//enumerateSecretIDs(context, objectDN, 0x00000110);

		}


		public string[] enumerateSecretIDs()
		{
			return enumerateSecretIDs(m_context, m_objectDN, 0x00000000);
		}

		private string[] enumerateSecretIDs(SSS_CONTEXT_T context, SS_OBJECT_DN_T targetObjDN, uint ssFlags)
		{
			int rcode = 0;
			uint count = 0;

			SS_SECRET_T secretIDList = new SS_SECRET_T();
			secretIDList.data = Marshal.AllocHGlobal(16384+1);
			secretIDList.len = 16384+1;

			SSCS_EXT_T ext = new SSCS_EXT_T();

			rcode = NSSSEnumerateSecretIDs(context,
				targetObjDN, 
				ssFlags,
				IntPtr.Zero, // [In, Out]	IntPtr					srchStr, 
				ref	count, 
				secretIDList, 
				ext);

			if (rcode != 0)
			{
				Console.Write("NSSSGetServiceInformation return error: " + rcode);
				return null;
			}

			string ids = Marshal.PtrToStringAnsi(secretIDList.data);

			if (ids.EndsWith("*"))
				ids = ids.Substring(0, ids.Length-1);
			
			// parse the buffer
			string [] split = null;	
			string delimStr = "*";
			char [] delimiter = delimStr.ToCharArray();


			for (int i = 1; i <= count; i++) 
			{
				split = ids.Split(delimiter, i);
			}

			/*
			if (false)
			{
				// dump em out.
				for (int i=0; i<split.Length; i++)
				{
					Console.WriteLine("-------------------");
					string sTemp = split[i].ToString().TrimEnd(delimiter);
					Console.WriteLine("ID: "+sTemp);

					if (sTemp.StartsWith("SS_CredSet"))
						getSharedSecret(context, targetObjDN, ssFlags, sTemp);
					else
						getSecret(context, targetObjDN, ssFlags, sTemp);
					Console.WriteLine();
				}
			}
			*/
			
			return split;			

		}

		public RemoteSecret getSecret(uint ssFlags, string sSecretID)
		{
			return getSecret(m_context, m_objectDN, ssFlags, sSecretID);
		}

		public RemoteSecret getSecret(SSS_CONTEXT_T context, 
			SS_OBJECT_DN_T targetObjDN, 
			uint ssFlags, 
			string sSecretID)
		{

			if (sSecretID.StartsWith("SS_CredSet") || sSecretID.StartsWith("SS_App"))
			{
				return getSharedSecret(context, targetObjDN, ssFlags, sSecretID);
			}


			int rcode = 0;
			SS_PWORD_T epPassword = new SS_PWORD_T();

			SSS_READEXT_T readData = new SSS_READEXT_T();

			SS_SECRET_ID_T secretId = new SS_SECRET_ID_T();
			secretId.id = sSecretID;
			secretId.len = sSecretID.Length+1;

			SS_SECRET_T secretValue = new SS_SECRET_T();
			secretValue.data = Marshal.AllocHGlobal(1024*64);
			secretValue.len = (1024*64);

			SSCS_EXT_T ext = new SSCS_EXT_T();
			rcode = NSSSReadSecret(context, targetObjDN, ssFlags, epPassword, readData, secretId, secretValue, ext);

			if (rcode == 0)
			{
				RemoteSecret rs = new RemoteSecret(sSecretID, readData.crtStamp, readData.lmtStamp, readData.latStamp);				
				String sValue = Marshal.PtrToStringAnsi(secretValue.data, secretValue.len);
				//String sValue = Marshal.PtrToStringAnsi(secretValue.data, secretValue.len);
				rs.setValue(sValue);	

				Marshal.FreeHGlobal(secretValue.data);
				return rs;
			}
			else
				Console.WriteLine("value: not found "+ rcode);

			// release unmanaged memory.
			Marshal.FreeHGlobal(secretValue.data);
			return null;

		}


		public static RemoteSecret getSharedSecret(SSS_CONTEXT_T context, 
			SS_OBJECT_DN_T targetObjDN, 
			uint ssFlags, 
			string sSecretID)
		{
			int rcode = 0;
			SS_PWORD_T epPassword = new SS_PWORD_T();

			SSS_READEXT_T readData = new SSS_READEXT_T();

			SS_SH_SECRET_ID_T pSharedSecretID = new SS_SH_SECRET_ID_T();
			if (sSecretID.StartsWith("SS_CredSet"))
			{
				pSharedSecretID.id = sSecretID.Substring(11);
				pSharedSecretID.len = pSharedSecretID.id.Length;
				pSharedSecretID.type = 2;
			}
			else  //SS_App
			{
				pSharedSecretID.id = sSecretID.Substring(7);
				pSharedSecretID.len = pSharedSecretID.id.Length;
				pSharedSecretID.type = 1;
			}

			SSCS_EXT_T ext = new SSCS_EXT_T();

			// get secretHandle
			IntPtr secretHandle = NSSSCreateSHSHandle();

			rcode = NSSSReadSharedSecret(secretHandle, pSharedSecretID, context, targetObjDN, ssFlags, epPassword, readData, ext);

			if (rcode == 0)
			{
				RemoteSecret rs = new RemoteSecret(pSharedSecretID.id, readData.crtStamp, readData.lmtStamp, readData.latStamp);
				// enumerate key/value pairs
				loadKeyValuePairs(context, secretHandle, rs);
				return rs;
			}
			else
				return null;
		}

		internal static void loadKeyValuePairs(SSS_CONTEXT_T context, IntPtr secretHandle, RemoteSecret rs)
		{
			int rcode=0;
			int iStart = 1;

			uint iKeyLen = 60416;		
			//StringBuilder sKey = new StringBuilder((int)iKeyLen);			
			IntPtr pKey; // = new IntPtr(4);
			pKey = Marshal.AllocHGlobal(60416);

			uint iValueLen = 60416;
			//StringBuilder sValue = new StringBuilder((int)iValueLen);
			IntPtr pValue; // = new IntPtr(4);
			pValue = Marshal.AllocHGlobal(60416);

			while (true)
			{			


				rcode = NSSSGetNextSHSEntry(
					iStart,
					secretHandle,
					ref iKeyLen,
					//sKey,
					pKey,
					ref iValueLen,
					//sValue,
					pValue,
					context.flags
					);

				if (rcode != 0) 
					break;
				else
				{										

					/*
					UTF8Encoding encoder = new UTF8Encoding();
					System.Text.Decoder decoder = encoder.GetDecoder();

					byte[] baKey= new byte[(int)iKeyLen];
					Marshal.Copy(pKey, baKey, 0, (int)iKeyLen-1);
					char[] caKey = new char[256];
					rcode = decoder.GetChars(baKey, 0, baKey.Length, caKey, 0);
					string sKey = new String(caKey);
				
					Console.WriteLine("baKeyBytes");
					for (int j=0; j<baKey.Length; j++)
						Console.Write(baKey[j] + " ");

					Console.WriteLine();

					byte[] baValue = new byte[(int)iValueLen];
					Marshal.Copy(pValue, baValue, 0, (int)iValueLen-1);
					char[] caValue = new char[256];
					rcode = decoder.GetChars(baValue, 0, baValue.Length, caValue, 0);
					string sValue = new string(caValue);
				
					Console.WriteLine("Adding key-value " + sKey.Trim() + "=" +sValue.Trim());

					rs.setKeyValuePair(sKey.Trim(), sValue.Trim());

					*/

					
					string sKey = Marshal.PtrToStringAnsi(pKey, (int)iKeyLen-1);				
					string sValue = Marshal.PtrToStringAnsi(pValue, (int)iValueLen-1);
					//Marshal.PtrToStringAnsi(
					//Console.WriteLine(sKey + "=" + sValue);				

					rs.setKeyValuePair(sKey, sValue);			
					

					iKeyLen = 60416;
					iValueLen = 60416;

					iStart = 0;
				}
			}	
			
			Marshal.FreeHGlobal(pKey);
			Marshal.FreeHGlobal(pValue);
		
		}


		public void setSecret(RemoteSecret secret)
		{
			setSecret(m_context, m_objectDN, 0, secret);
		}

		internal void setSecret(SSS_CONTEXT_T context, 
			SS_OBJECT_DN_T targetObjDN, 
			uint ssFlags,
			RemoteSecret secret)
		{
			int rcode = 0;
			// create a secretHandle
			// get secretHandle
			IntPtr secretHandle = NSSSCreateSHSHandle();

			// write out the keyValue Pairs
			System.Collections.Specialized.NameValueCollection nvc = secret.getKeyValueCollection();

			//int		NSSS_LDAP_CTX_F					0x00000002; 	//* Context is for LDAP
			//int 	   NSSS_CONTEXT_INITIALIZED_F		0x00000004;
			for (int i=0; i<nvc.Count; i++)
			{
				string sKey = nvc.GetKey(i);
				string sValue = nvc.Get(i);
				rcode = NSSSAddSHSEntry(secretHandle, sKey, sValue, 6);
			}


			SS_PWORD_T epPassword = new SS_PWORD_T();
						
			SS_SH_SECRET_ID_T pSharedSecretID = new SS_SH_SECRET_ID_T();
			pSharedSecretID.id = secret.getID();
			pSharedSecretID.len = pSharedSecretID.id.Length;
			pSharedSecretID.type = 2;

			SSCS_EXT_T ext = new SSCS_EXT_T();

			// Write the SharedSecret
			rcode = NSSSWriteSharedSecret(secretHandle, 
					pSharedSecretID, 
					context, 
					targetObjDN, 
					ssFlags, 
					epPassword, 
					ext);


		}
	}
}