using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Security.Permissions;

namespace Util.Security
{
        public class Impersonator: IDisposable
        {
        #region Constants
        private const Int32 IMPERSONATION_LEVEL = 2;
        private const Int32 LOGON32_PROVIDER_DEFAULT = 0;
        private const Int32 LOGON32_LOGON_INTERACTIVE = 3;
        #endregion

        #region Members
        private WindowsImpersonationContext _nusr = null;
        #endregion

        #region WinApi
        [DllImport("kernel32.dll", CharSet=CharSet.Auto)]
        public extern static Boolean CloseHandle(
            IntPtr Handle);

        [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
        public extern static Boolean DuplicateToken(
            IntPtr ExistingTokenHandle,
            Int32 SECURITY_IMPERSONATION_LEVEL,
            ref IntPtr DuplicateTokenHandle);

        [DllImport("advapi32.dll", SetLastError=true)]
        public static extern bool LogonUser(
            String lpszUsername,
            String lpszDomain,
            String lpszPassword,
            Int32 dwLogonType,
            Int32 dwLogonProvider,
            ref IntPtr phToken);
        #endregion

        #region Methods
        #region Constructors
        public Impersonator(
            String user,
            String password):
            this(
            Environment.MachineName,
            user,
            password)
        {
        }

        public Impersonator(
            String domain,
            String user,
            String password)
        {
            _nusr = GetImpersonatedUser(
                domain,
                user,
                password);
        }
        #endregion

        #region Destructor
        ~Impersonator()
        {
            Dispose();
            GC.SuppressFinalize(this);
        }
        #endregion

        #region Interface
        public void Dispose()
        {
            if(_nusr != null)
            {
                _nusr.Undo();
                _nusr = null;
            }
        }
        #endregion

        #region Implementation
        private WindowsImpersonationContext GetImpersonatedUser(
            String domain,
            String user,
            String password)
        {
            IntPtr  exst = new IntPtr(0);
            IntPtr  dupl = new IntPtr(0);

            Boolean resl = true;

            WindowsIdentity             widt = null;
            WindowsImpersonationContext wctx = null;

            exst = IntPtr.Zero;
            dupl = IntPtr.Zero;

            try
            {
                resl = LogonUser(
                    user,
                    domain,
                    password,
                    LOGON32_LOGON_INTERACTIVE,
                    LOGON32_PROVIDER_DEFAULT,
                    ref exst);

                if (!resl)
                {
                    throw new System.ComponentModel.Win32Exception(
                        Marshal.GetLastWin32Error());
                }

                resl = DuplicateToken(
                    exst,
                    IMPERSONATION_LEVEL,
                    ref dupl);

                if (!resl)
                {
                    CloseHandle(exst);
                    throw new System.ComponentModel.Win32Exception(
                        Marshal.GetLastWin32Error());
                }
                else
                {
                    widt = new WindowsIdentity(dupl);
                    wctx = widt.Impersonate();

                    return wctx;
                }
            }
            catch(Exception ex)
            {
                String msgs = ex.Message;
                return null;
            }
            finally
            {
                if (exst!= IntPtr.Zero)
                {
                    CloseHandle(exst);
                }
                if (dupl != IntPtr.Zero)
                {
                    CloseHandle(dupl);
                }
            }
        }
        #endregion
        #endregion
    }
}