/***********************************************************************
 * 
 *  Copyright (C) 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.
 * 
 *  Author: Jim Norman
 *
 ***********************************************************************/

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

namespace Novell.Casa.Client.Auth
{
    /// <summary>
    /// Summary description for Class1.
    /// </summary>
    public class Authtoken
    {
        private const string AUTH_LIBRARY = "casa_authtoken";
        private const int BUFFER_OVERFLOW = 6;

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
        private struct LUID
        {   
            public int luidLow;
            public int luidHigh;
        }

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

        [DllImport(AUTH_LIBRARY, CharSet=CharSet.None) ]
        private static extern int ObtainAuthToken
        (
            [In]        byte[]          baService,      
            [In]        byte[]          baHost,
            [In, Out]   byte[]          baToken,                            
            [In, Out]   ref int         iTokenLength
        );

        [DllImport(AUTH_LIBRARY, CharSet=CharSet.None) ]
        private static extern int ObtainAuthTokenEx
        (
            [In]        byte[]          baService,      
            [In]        byte[]          baHost,
            [In, Out]   byte[]          baToken,                            
            [In, Out]   ref int         iTokenLength,
            [In]        SSCS_EXT_T      ext
        );


        public Authtoken()
        {
        }

        public static string ObtainAuthTokenAsString(string sService, string sHost)
        {
            byte[] baToken;
            baToken = ObtainAuthToken(sService, sHost, null);
            if (baToken != null)
            {
                return (Encoding.ASCII.GetString(baToken));
            }
            else
            {
                return null;
            }
        }

        public static byte[] ObtainAuthToken(string sService, string sHost)
        {
            return ObtainAuthToken(sService, sHost, null);
        }

        private static byte[] ObtainAuthToken(string sService, string sHost, WinLuid luid)
        {
            int rcode = 0;
            byte[] baService = null;
            byte[] baHost =  null;          
            int bufferSize = 0;         
            bool bLuidPassedIn = false;

            byte[] baToken = new byte[bufferSize];

            // convert service to ascii byte array
            if (sService != null)
            {
                baService = Encoding.ASCII.GetBytes(sService);
            }
            else
            {
                throw new Exception("Invalid parameter");
            }

            // convert host to ascii byte array
            if (sHost != null)
            {
                baHost = Encoding.ASCII.GetBytes(sHost);
            }
            else
            {
                throw new Exception("Invalid parameter");
            }
            

            SSCS_EXT_T ext = new SSCS_EXT_T();
            LUID sluid;
            if ((luid != null) &&
                ((luid.GetHighPart() != 0) || (luid.GetLowPart() != 0)))
            {
                // allocate a structure to marshal
                sluid = new LUID();
                sluid.luidHigh = luid.GetHighPart();
                sluid.luidLow = luid.GetLowPart();
                
                ext.extID = 1;
                ext.version = 1;
                ext.ext = Marshal.AllocHGlobal(Marshal.SizeOf(sluid));

                Marshal.StructureToPtr(sluid, ext.ext, false);
                bLuidPassedIn = true;
            }

            
            // call with buffersize of 0.  This way we determine the exact size.
            try
            {
                if (bLuidPassedIn)
                {   
                    rcode = ObtainAuthTokenEx(baService, baHost, baToken, ref bufferSize, ext);
                }
                else
                {
                    rcode = ObtainAuthToken(baService, baHost, baToken, ref bufferSize);
                }
                
                if ((rcode & 0xFFFF) == BUFFER_OVERFLOW)
                {               
                    // now allocate the proper size
                    baToken = new byte[bufferSize];
                    if (bLuidPassedIn)
                    {
                        rcode = ObtainAuthTokenEx(baService, baHost, baToken, ref bufferSize, ext);
                    }
                    else
                    {
                        rcode = ObtainAuthToken(baService, baHost, baToken, ref bufferSize);
                    }
                }
            }
            catch (Exception e)
            {
                LogMessage(e.ToString());
                return null;
            }
                                    
            if (ext.ext != IntPtr.Zero)
                Marshal.FreeHGlobal(ext.ext);

            if (rcode != 0)
            {               
                throw new Exception(rcode.ToString());
            }
            else
            {
                return baToken;
            }           
        }

        private static void LogMessage(string sMessage)
        {
            System.Diagnostics.Trace.WriteLine("(C#)AuthToken: " + sMessage);
        }
    }
}