/***********************************************************************
 * 
 *  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 System.Collections;
using System.Xml;
using System.IO;
using Novell.CASA;
using Novell.CASA.DataEngines.Common;
using System.Collections.Specialized;
using System.Runtime.InteropServices;
using Novell.CASA.DataEngines.KWallet;




namespace Novell.CASA.DataEngines
{

   /*
    * This class is implementation of Data engine for KWallet.
    */


    class KWalletEngine : DataEngine
    {

      string[] EntryTypes = {"Binary","Passwords","Unknown", "Maps"};   
      enum KWalletResult : int
      {
        KWALLET_RESULT_OK,
                                                                              
        KWALLET_RESULT_CANNOT_OPEN_WALLET,
        KWALLET_RESULT_CANNOT_OPEN_FOLDER,
        KWALLET_RESULT_CANNOT_WRITE_ENTRY,
        KWALLET_RESULT_MALFORMED_XML,
        KWALLET_RESULT_CANNOT_CREATE_FOLDER,
        KWALLET_RESULT_CANNOT_CREATE_WALLET,
        KWALLET_RESULT_CANNOT_REMOVE_ENTRY,
        KWALLET_RESULT_UNKNOWN_ERROR
      };


       public KWalletEngine()
       {
           // TBD: Read Policy information and have a list of wallet names;

       }

       public XmlNode Aggregate()
       {
              XmlDocument doc = new XmlDocument();
              Hashtable lookup  = new Hashtable();
              XmlElement key1;
              XmlAttribute Atr;
              XmlElement value1;
              XmlAttribute idAttr;
              String secid, val;
              XmlElement currentWallet;
              XmlElement Folder;
              XmlElement Type;
              XmlElement Secret;
              String walletName, foldername, entryType, secretval;

              

             //Adding Kwallet Top Node
 	     XmlElement elem = doc.CreateElement(ConstStrings.CCF_KW);
             doc.AppendChild(elem);
                  
             EnumSecretList enumList = new EnumSecretList();
	     //kwallet.Try(enumList);
	     kwallet.AggregateKW(enumList);
             EnumSecretList tempEnumSecretList1 = enumList;

             //This can be Null only when nothing is aggregated.
             if (((String)Marshal.PtrToStringAnsi(tempEnumSecretList1.walletName)) == null ) 
             {
                  //TBD: Log that there are no secrets to aggregate
             }
             else // Something to Aggregate
             { 
                          
                         
             while (tempEnumSecretList1 != null)
	     {
			walletName = (String)Marshal.PtrToStringAnsi(tempEnumSecretList1.walletName);
		       // Console.WriteLine("\n\nWallet name is***#  : "+walletName);

		        foldername = (String)Marshal.PtrToStringAnsi(tempEnumSecretList1.folderName);
		        //Console.WriteLine("\tFolder***# : "+foldername);
                        int entrytype = tempEnumSecretList1.entryType;
		        //Console.WriteLine("\t\tEntryType ***#: "+entrytype);
                        entryType = EntryTypes[entrytype]; 
		        //Console.WriteLine("\t\tEntryType in string ***#: "+entryType);
                   
		        secretval = (String)Marshal.PtrToStringAnsi(tempEnumSecretList1.secretVal);
		        //Console.WriteLine("\t\tSecret***# : "+secretval);

                        //Adding Wallet
                        if (lookup.ContainsKey(walletName))
                        {
				//Console.WriteLine("Wallet Node found");
                                currentWallet = (XmlElement)lookup[walletName];
                        }
                        else
                        {       
                                currentWallet = doc.CreateElement(ConstStrings.CCF_WALLET);
                                idAttr = doc.CreateAttribute(ConstStrings.CCF_ID);
                                idAttr.Value = walletName;
                                currentWallet.SetAttributeNode(idAttr);
                                elem.AppendChild(currentWallet);
				lookup.Add(walletName,currentWallet);
                        }

                        //Adding Folder
                        String xpath = "descendant::Folder[@Name='"+foldername+"']";
                        XmlNodeList folList = currentWallet.SelectNodes(xpath);
                        if (folList.Count == 0)
                        {
                                 Folder = doc.CreateElement(ConstStrings.CCF_FOLDER);
                                 XmlAttribute name_attr = doc.CreateAttribute(ConstStrings.CCF_NAME);
                                 name_attr.Value = foldername;
                                 Folder.SetAttributeNode(name_attr);
                                 currentWallet.AppendChild(Folder);
                                         
                         } 

                                
                        //Adding Type   
                        xpath = "descendant::Folder";
                        XmlNodeList folderlist = currentWallet.SelectNodes(xpath);

                        foreach(XmlNode folder in folderlist)
                        {
                        	XmlAttributeCollection atcol =  folder.Attributes;
                                XmlAttribute attr = atcol[ConstStrings.CCF_NAME];
                                if (attr.InnerXml.Equals(foldername))
                                {
                                	xpath = "descendant::Type[@ID='"+entryType+"']"; 
                                        XmlNodeList keylist = folder.SelectNodes(xpath);
                                        if (keylist.Count == 0)
                                	{
                                             	 Type = doc.CreateElement(ConstStrings.CCF_TYPE);
	                                         XmlAttribute name_attr = doc.CreateAttribute(ConstStrings.CCF_ID);
                	                         name_attr.Value = entryType;
		                                 Type.SetAttributeNode(name_attr);
	                                         folder.AppendChild(Type);

                                        }
                                        else
                                        {
                                         //Console.WriteLine("Type Already Added");
                                        }    

                                 }
                                      
                        }



                       //Adding the Secret
                       xpath = "descendant::Folder";
                       folderlist = currentWallet.SelectNodes(xpath); 
                              
                       foreach(XmlNode folder in folderlist)
                       {
                       		XmlAttributeCollection atcol =  folder.Attributes;
                                XmlAttribute attr = atcol[ConstStrings.CCF_NAME];
                                if (attr.InnerXml.Equals(foldername))
                                {
                                	xpath = "descendant::Type[@ID='"+entryType+"']";
					
                                        XmlNodeList keylist = folder.SelectNodes(xpath);
                                        if (keylist.Count == 0)
                                        {
                                                 //Console.WriteLine("Construction of CCF Failed");
                                        }
                                        else
                                        {
					
                                        	XmlNode TargetType = keylist[0]; //Type Node
                                                string[] split = null; 
                                               
                                                int index = secretval.IndexOf('=');
                                                secid = secretval.Substring(0,index);


                                                Secret = doc.CreateElement(ConstStrings.CCF_SECRET);
                                                XmlAttribute  idattr = doc.CreateAttribute(ConstStrings.CCF_ID);
                                                idattr.Value = secid;
                                                Secret.SetAttributeNode(idattr);
						
				                /*XmlAttribute typeAttr = doc.CreateAttribute(ConstStrings.CCF_TYPE);
			                        typeAttr.Value = entryType;
				                Secret.SetAttributeNode(typeAttr);
                                                 */



	                                        if (entryType.Equals("Maps"))
        	                                {
                                                    string delim = ";";
                                                    char[] delimiter = delim.ToCharArray();
                                                    string realval = secretval.Substring(index+1);
                                                    
                                                    for(int x = 1; x < 10 ; x++)
                                                    {
							split = realval.Split(delimiter, x);	
                                                    }
                                                    foreach(string s in split)	
                                                    { 
                                                        int ix; 
							string key;
							string value;  
                                                          
							//Console.WriteLine("The val is :" + s);
                                                        if (s.Equals(""))
                                                        { 
                                                                //Console.WriteLine("No Secret Content for a Secret ID");  
								key = " ";
								value = " ";
                                                        }
                                                        else
                                                        {
								ix = s.IndexOf(':');
                                                       		key = s.Substring(0,ix);
								value = s.Substring(ix+1);

                                                        }  

                                                        key1 = doc.CreateElement(ConstStrings.CCF_KEY);
                                                        Atr = doc.CreateAttribute(ConstStrings.CCF_ID);
                                                        Atr.Value = key;
                                                        key1.SetAttributeNode(Atr);
                                                        
                                                         //Value
                                                        value1 = doc.CreateElement(ConstStrings.CCF_VALUE);
                                                        value1.InnerText = value;
                                                        key1.AppendChild(value1);
                                                        Secret.AppendChild(key1);
                                 
                                                       TargetType.AppendChild(Secret);
                                                    }
                	                        }//entrytype=Maps
                        	                else if (entryType.Equals("Passwords"))
                                	        {
                                                	//Console.WriteLine("Passwords");
	                                                val = secretval.Substring(index+1);
	
        	                                        //Key
                	                                key1 = doc.CreateElement(ConstStrings.CCF_KEY);
                        	                        Atr = doc.CreateAttribute(ConstStrings.CCF_ID);
                                	                Atr.Value = "Credential";
                                        	        key1.SetAttributeNode(Atr);
	
        	                                       //Value
                	                               value1 = doc.CreateElement(ConstStrings.CCF_VALUE);
                        	                       value1.InnerText = val;
                                	               key1.AppendChild(value1);
                                        	       Secret.AppendChild(key1);

     		                                       TargetType.AppendChild(Secret);
                                               }//entryType=Password
					}
                                 }
                          }

                              

        	       if (tempEnumSecretList1.next == IntPtr.Zero)
		       {
		         // Console.WriteLine("Reached End ##");
	        	  break;
		       }

        	       tempEnumSecretList1 = (EnumSecretList)Marshal.PtrToStructure(tempEnumSecretList1.next, typeof(EnumSecretList));

	      }
              }
       kwallet.FreeResources();   
        #if TEST
            XmlTextWriter writer = new XmlTextWriter("/root/kwtest.xml",null);
            writer.Formatting = Formatting.Indented;
            doc.Save(writer);
            writer.Close();
        #endif



       return doc.ChildNodes[0];


       }


	public  int SetSecret(XmlNode secret)
	{
             try
             { 
		
        	     string walletName = ExtractWalletName(secret);
	             string folderName = ExtractFolderName(secret);
	             string keyName = ExtractKeyName(secret);
        	     int secretType = ExtractSecretType(secret);
		     
		     if (secretType != 3) //Type not Map
             	     {
		
	                string value = secret.ChildNodes[0].ChildNodes[0].InnerText; //Secret.Key.Value
		
			return(kwallet.SetSecret(walletName, folderName, secretType, keyName, value, value.Length));		
             	     }               
	             else //If type is Map
        	     {
		
                	NameValueCollection nvc = new NameValueCollection();
		
			for (int i =0; i< secret.ChildNodes.Count; i++)
        	        {
		
                	        XmlNode key = secret.ChildNodes[i];
                        	XmlAttributeCollection atcol;
	                        atcol =  key.Attributes;
        	                String keyMapName = atcol["ID"].InnerXml;
		
				string value = key.ChildNodes[0].InnerText; //Secret.Key.Value
 		
       	                nvc.Add(keyMapName,value); 
                	}
              
		
                	return(kwallet.SetSecret(walletName, folderName,keyName,nvc));
		     }
             }
             catch(NullReferenceException n)
             {
		//Console.WriteLine("Exception in Set Secret Cause :" + n.ToString());
                return (int)KWalletResult.KWALLET_RESULT_MALFORMED_XML;
	
	     }
             catch(Exception e)
             {
		//Console.WriteLine("Exception in Set Secret Cause :" + e.ToString());
                return (int)KWalletResult.KWALLET_RESULT_UNKNOWN_ERROR;
             }
        }
        
	public int SetSecret(XmlNode secret, int opnType)
        {
		return SetSecret(secret);
        }



        public  int GetSecret(XmlNode secret)
        {
        	return ConstStrings.CASA_SUCCESS;
        }

         public  int Remove(XmlNode secret)
         {
	      try
              {   
		     string walletName = ExtractWalletName(secret);
        	     string folderName = ExtractFolderName(secret);
	             string keyName = ExtractKeyName(secret);
	     	     int res = kwallet.DeleteSecret(walletName, folderName, keyName); 	     
             	     if (res == 0)
                     {
			XmlNode root = secret.ParentNode;
        	        root.RemoveChild(secret);
             	     }
             	     return res;
             }
             catch(NullReferenceException n)
             {
                //Console.WriteLine("Exception in Set Secret Cause :" + n.ToString());
                return (int)KWalletResult.KWALLET_RESULT_MALFORMED_XML;
                                                                                                                                
             }
             catch(Exception e)
             {
                //Console.WriteLine("Exception in Set Secret Cause :" + e.ToString());
                return (int)KWalletResult.KWALLET_RESULT_UNKNOWN_ERROR;
             }
         }

	string ExtractWalletName(XmlNode secret)
        {
  	    XmlAttributeCollection atcol;
            XmlNode parentNode = secret.ParentNode.ParentNode.ParentNode;
            atcol =  parentNode.Attributes;
            String walletname = atcol["ID"].InnerXml;
            return walletname;
        }

	string ExtractFolderName(XmlNode secret)
        {
  	    XmlAttributeCollection atcol;
            XmlNode parentNode = secret.ParentNode.ParentNode; //Folder.Type.Secret
            atcol =  parentNode.Attributes;
            String foldername = atcol["Name"].InnerXml;
            return foldername;
        }

	string ExtractKeyName(XmlNode secret)
        {
  	    XmlAttributeCollection atcol;
            atcol =  secret.Attributes;
            String keyname = atcol["ID"].InnerXml;
            return keyname;
        }

	int ExtractSecretType(XmlNode secret)
        {
  	    XmlAttributeCollection atcol;
            XmlNode parentNode = secret.ParentNode; //Type.Secret
            atcol =  parentNode.Attributes;
            String entryType = atcol["ID"].InnerXml;
            if (entryType.CompareTo("Passwords")== 0) 
            {
		return 1;
            }        
            if (entryType.CompareTo("Binary") == 0) 
            {
		return 2;
            }        
            if (entryType.CompareTo("Maps") == 0) 
            {
		return 3;
            }        
            return 0;
        }
	
	public static Boolean IsStoreAvailable()
	{
		try
		{
			System.Runtime.InteropServices.Marshal.PrelinkAll(typeof(kwallet));
			return true;
		}
		catch(DllNotFoundException d)
		{
			//Console.WriteLine("Store Not Available Exception =" + d.ToString());
			return false;
		}
		catch(Exception e)
		{
			//Console.WriteLine("Store Not Available Exception =" + e.ToString());
			return false;
		}
	}
		
	
#if TEST
	public static void Main()
        {
                KWalletEngine kw = new KWalletEngine();

		
                                Console.WriteLine();
                                Console.WriteLine("********** Menu ***********");
                                Console.WriteLine("* 1. Add secret           *");
                                Console.WriteLine("* 2. Modify secret        *");
                                Console.WriteLine("* 3. Set secret           *");
                                Console.WriteLine("* 4. Remove  secret       *");
                                Console.WriteLine("* 5. Refresh              *");
                                Console.WriteLine("* 6. Quit                 *");
                                Console.WriteLine("***************************");
				Console.WriteLine("For all options the input is the file /root/kwtest.xml");



                                Console.WriteLine("Select option and Press enter");
                                String line = Console.ReadLine();
                                int res = 0;

                                if (line.Length > 0)
                                {
                                        char[] c = line.Substring(0, 1).ToCharArray();
                                        if (c.Length > 0)
                                        {
                                                if (c[0].Equals('6'))
                                                        return;
                                                if (c[0].Equals('5'))
			                                kw.Aggregate ();
                                                else
                                                {

							XmlDocument xmlDoc  = new XmlDocument();
					                XmlTextReader tr = new XmlTextReader("/root/kwtest.xml");
					                tr.Read();
					                xmlDoc.Load(tr);
					                XmlNode root =  xmlDoc.FirstChild;
					                if (root == null)
                					{
					                        Console.WriteLine("Root is null");
					                 }
					                XmlNode secret  = root.ChildNodes[0].ChildNodes[0].ChildNodes[0].ChildNodes[0];
  
                                                if (c[0].Equals('4'))
                                                        res =kw.Remove(secret);
                                                else if (c[0].Equals('1'))
                                                        res = kw.SetSecret(secret,ConstStrings.OPERATION_ADD_SECRET);
                                                else if (c[0].Equals('2'))
                                                        res = kw.SetSecret(secret,ConstStrings.OPERATION_MODIFY_SECRET);
                                                else if (c[0].Equals('3'))
                                                        res = kw.SetSecret(secret);
                                              }
                                        }
				}
                                Console.WriteLine("Result of Operation = " +  res);

        }
	#endif


    }
}