//
// Don't forget to remove the HTML tags above and at the end of the file
using System;
using System.Collections;
using System.Xml;
using System.DirectoryServices;
using System.Runtime.InteropServices;

/// 
/// Example of LDAP Authentication code using ADSI with MCIT custom error messages
/// written by Dmitriy Kashchenko, IDM team
public class LdapAuthetication
{
    // const
    private const string ERR_NOT_FOUND = "(0x80072030): not found";

    // LDAP metrics
    private string _ldapserver = "LDAP://ldap.ent.med.umich.edu:636/";
    private string _topContainer = "dc=med,dc=umich,dc=edu";
    private String _defaultFilter = "(uid={0})";
     
    // errors handling
    // messages file name
    private string _messages = "ldapautherrors.xml";
    // variables
    private string _errorMsg;
    private string _provider;
    private const string nodeKey = "message";
    private const string indexAttr = "index";
    private Hashtable errors = null;

    private void parseFile(string fname)
    {
        XmlTextReader node = new XmlTextReader(fname);
        node.WhitespaceHandling = WhitespaceHandling.None;

        while (node.Read())
        {
           if (node.NodeType == XmlNodeType.Element && node.Name.Equals(nodeKey))
           {
               if (node.HasAttributes) 
               {
                   string index = node.GetAttribute("index");
                   node.Read();
                   string message = node.Value;
                   errors.Add(index, message);
                }
            }
        }

        node.Close();
    }

    [DllImport("activeds.dll", ExactSpelling = true, EntryPoint = "ADsGetLastError", CharSet = System.Runtime.InteropServices.CharSet.Unicode)]
    private static extern int ADsGetLastError(ref int error, IntPtr errorbuf, int errorbuflen, IntPtr namebuf, int namebuflen);

    private int getExtendedError()
    {
        IntPtr errorbuf = (IntPtr)0;
        IntPtr namebuf = (IntPtr)0;
        int error = 0;

        try
        {
            errorbuf = Marshal.AllocHGlobal(256 * 2);
            namebuf = Marshal.AllocHGlobal(256 * 2);
            ADsGetLastError(ref error, errorbuf, 256, namebuf, 256);
            _errorMsg = Marshal.PtrToStringUni(errorbuf);
            _provider = Marshal.PtrToStringUni(namebuf);

            return error;
        }
        finally
        {
            if (errorbuf != (IntPtr)0) Marshal.FreeHGlobal(errorbuf);
            if (namebuf != (IntPtr)0) Marshal.FreeHGlobal(namebuf);
        }
    }

    public string getErrorMessage(Exception e)
    {
        string rc = e.ToString();
        if (errors != null)
        {
           int ee = getExtendedError();
           if (ee != 0) rc = _errorMsg;
           foreach (DictionaryEntry entry in errors)
           {
              string index = (string)entry.Key;
              if (rc.IndexOf(index) != -1)
              {
                  rc = (string)entry.Value;
                  break;
              }
           }
        }

        return rc;
    }

    public LdapAuthetication()
    {
        errors = new Hashtable();
        parseFile(_messages);
    }

    public DirectoryEntry Authenticate(string user, string password)
    {
        DirectoryEntry de = new DirectoryEntry(_ldapserver+_topContainer);
        // required to initate anonymous bind
        de.AuthenticationType = System.DirectoryServices.AuthenticationTypes.ServerBind;
        DirectorySearcher searcher = new DirectorySearcher(de);

        //set the filer. the filter syntax is the standard LDAP filter syntax
        searcher.Filter = string.Format(_defaultFilter, user);
        SearchResult result = searcher.FindOne();
        if (result == null) throw new Exception(ERR_NOT_FOUND);

        de = result.GetDirectoryEntry();
        // Distingushed Name of the found account
        string DN = de.Path.Substring (de.Path.ToUpper().IndexOf("CN="));
        // Close search connection
        de.Close();

        // now bind
        de = new DirectoryEntry(_ldapserver+_topContainer);
        de.Username = DN;
        de.Password = password;
        de.AuthenticationType = System.DirectoryServices.AuthenticationTypes.SecureSocketsLayer;
        Object obj = de.NativeObject;

        return de;
    }

    public static void Main(string[] args)
    {
        LdapAuthetication auth = new LdapAuthetication();
        if (args.Length == 2)
        {
            try
            {
                DirectoryEntry de = auth.Authenticate(args[0], args[1]);
                Console.WriteLine("Authentication Succesful: " + de.Username);
            }
            catch (Exception e)
            {
                Console.WriteLine(auth.getErrorMessage(e));
            }
        }
        else
        {
            Console.WriteLine("Usage: LdapAuthentication Username Password");
        }
    }
}
//