/***********************************************************************
 * 
 *  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 AppModule.NamedPipes;
using System.Runtime.InteropServices;
using System.Text;

using HANDLE = System.IntPtr;

namespace AppModule.NamedPipes 
{
	#region Comments
	/// <summary>
	/// A utility class that exposes named pipes operations.
	/// </summary>
	/// <remarks>
	/// This class uses the exposed exposed kernel32.dll methods by the 
	/// <see cref="AppModule.NamedPipes.NamedPipeNative">NamedPipeNative</see> class
	/// to provided controlled named pipe functionality.
	/// </remarks>
	#endregion
	public sealed class ImpersonateWrapper 
	{

		public const int TOKEN_QUERY = 0X00000008;

		const int ERROR_NO_MORE_ITEMS = 259;



		// Client USERID stuff
		// 1. call ImpersonateNamedPipeClient(hPipe)
		// 2. call OpenThreadToken(GetCurrentThread(),
		//							TOKEN_QUERY | TOKEN_QUERY_SOURCE,
		//							FALSE,
		//							phUserToken);

		public static int ImpersonateNamedPipeClient(IntPtr hPipeHandle)
		{
			int rcode = ImpersonateNative.ImpersonateNamedPipeClient(hPipeHandle);					
			return rcode;
		}


		static int PerformDump(HANDLE token)
		{
			StringBuilder sb = new StringBuilder();
			ImpersonateNative.TOKEN_USER tokUser;
			const int bufLength = 256;
			IntPtr tu = Marshal.AllocHGlobal( bufLength );
			int cb = bufLength;
			if (ImpersonateNative.GetTokenInformation( token, ImpersonateNative.TOKEN_INFORMATION_CLASS.TokenUser, tu, cb, ref cb ))
				Console.WriteLine("GetTokenInformation successful");
			else
			{
				Console.WriteLine("GetTokenInformation NOT successful");
				uint error = NamedPipeNative.GetLastError();
				Console.WriteLine("error" + error.ToString());
			}

			tokUser = (ImpersonateNative.TOKEN_USER) Marshal.PtrToStructure(tu, typeof(ImpersonateNative.TOKEN_USER) );
			//sb.Append(DumpAccountSid(tokUser.User.Sid));
			IntPtr pUserID = tokUser.User.Sid;
			//Console.WriteLine("UserID: " + pUserID);

			DumpAccountSid(pUserID);
			Marshal.FreeHGlobal( tu );


			tu = Marshal.AllocHGlobal(bufLength);
			cb = bufLength;

			// get token states
			ImpersonateNative.TOKEN_STATISTICS stats;			

			if (ImpersonateNative.GetTokenInformation(token, ImpersonateNative.TOKEN_INFORMATION_CLASS.TokenStatistics, tu, cb, ref cb))
			{			
				stats = (ImpersonateNative.TOKEN_STATISTICS) Marshal.PtrToStructure(tu, typeof(ImpersonateNative.TOKEN_STATISTICS));	
				Console.WriteLine("UserLow: "+stats.AuthenticationId.LowPart.ToString());
				Console.WriteLine("UserHigh: "+stats.AuthenticationId.HighPart.ToString());
			}
			else
			{
				Console.WriteLine("failed");				
			}
			
			return (int)pUserID;
		}


		static string DumpAccountSid(IntPtr SID)
		{
			int cchAccount = 0;
			int cchDomain = 0;
			int snu = 0 ;
			StringBuilder sb = new StringBuilder();

			// Caller allocated buffer
			StringBuilder Account= null;
			StringBuilder Domain = null;
			bool ret = ImpersonateNative.LookupAccountSid(null, SID, Account, ref cchAccount, Domain, ref cchDomain, ref snu);
			if ( ret == true )
				if ( Marshal.GetLastWin32Error() == ERROR_NO_MORE_ITEMS )
					return "Error";
			try
			{
				Account = new StringBuilder( cchAccount );
				Domain = new StringBuilder( cchDomain );
				ret = ImpersonateNative.LookupAccountSid(null, SID, Account, ref cchAccount, Domain, ref cchDomain, ref snu);
				if (ret)
				{
					sb.Append(Domain);
					sb.Append(@"\\");
					sb.Append(Account);
				}
				else
					Console.WriteLine("logon account (no name) ");
			}
			catch (Exception ex)
			{
				Console.WriteLine(ex.Message);
			}
			finally
			{
			}
			string SidString = null;
			ImpersonateNative.ConvertSidToStringSid(SID, ref SidString);
			sb.Append("\nSID: ");
			sb.Append(SidString);			

			
			Console.WriteLine("Acct info: "+ sb.ToString());
			return sb.ToString();
		}

		public static int GetLocalUserID(PipeHandle handle, ref int lowPart, ref int highPart, ref string SidString)
		{			
			int rcode = -1;
			// get client userID
			int code = ImpersonateNative.ImpersonateNamedPipeClient(handle.Handle);
			if (code == 0)
			{
				uint lastError = NamedPipeNative.GetLastError();
				Console.WriteLine("ImpersonateNamedPipeClient Error: "+rcode.ToString());
				return -1;
			}

			try 
			{
				IntPtr hThread = ImpersonateNative.GetCurrentThread();
				uint iDesiredInfo = 24; //TOKEN_QUERY | TOKEN_QUERY_SOURCE;
				IntPtr userToken = Marshal.AllocHGlobal(4);

				if (ImpersonateNative.OpenThreadToken(hThread, iDesiredInfo, true, out userToken))
				{

					StringBuilder sb = new StringBuilder();
					ImpersonateNative.TOKEN_USER tokUser;
					const int bufLength = 256;
					IntPtr tu = Marshal.AllocHGlobal( bufLength );
					int cb = bufLength;
					if (ImpersonateNative.GetTokenInformation( userToken, ImpersonateNative.TOKEN_INFORMATION_CLASS.TokenUser, tu, cb, ref cb ))
					{					
						tokUser = (ImpersonateNative.TOKEN_USER) Marshal.PtrToStructure(tu, typeof(ImpersonateNative.TOKEN_USER) );				
						IntPtr pUserID = tokUser.User.Sid;								
						Marshal.FreeHGlobal( tu );

						// get SID
						//string SidString = null;
						ImpersonateNative.ConvertSidToStringSid(pUserID, ref SidString);

						// get token states
						tu = Marshal.AllocHGlobal(bufLength);
						cb = bufLength;
						ImpersonateNative.TOKEN_STATISTICS stats;			
						if (ImpersonateNative.GetTokenInformation(userToken, ImpersonateNative.TOKEN_INFORMATION_CLASS.TokenStatistics, tu, cb, ref cb))
						{					
							stats = (ImpersonateNative.TOKEN_STATISTICS) Marshal.PtrToStructure(tu, typeof(ImpersonateNative.TOKEN_STATISTICS));
							// copy low and high part										
							lowPart = stats.AuthenticationId.LowPart;
							highPart = stats.AuthenticationId.HighPart;	
							rcode = -1;		
						}
					}
					else
					{
						Console.WriteLine("GetTokenInformation NOT successful");
						uint error = NamedPipeNative.GetLastError();
						Console.WriteLine("error" + error.ToString());				
					}

					// close handle
					ImpersonateNative.CloseHandle(hThread);
					ImpersonateNative.RevertToSelf();
				}
				else
				{
					int lastError = Marshal.GetLastWin32Error();
					uint errorcode = NamedPipeNative.GetLastError();
					Console.WriteLine("OpenThreadToken Error: "+ errorcode.ToString() + " code2: "+rcode.ToString());
				}
			}
			catch (Exception ex)
			{
				int error = Marshal.GetLastWin32Error();
				Console.WriteLine(ex.ToString());
				return rcode;
			}
			// end

			return rcode;

		}
	}
}