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


namespace Novell.CASA.DataEngines
{

   /*
    * This class is implementation of Data engine for Gnome-Keyring.
    */

    class GKEngine : DataEngine
    {

	enum KeyringResultExtended {
		GNOME_KEYRING_RESULT_NO_SUCH_ITEM = 128,
	        GNOME_KEYRING_RESULT_CANNOT_DELETE_SECRET_VALUE,
        	GNOME_KEYRING_RESULT_MALFORMED_XML,
        	GNOME_KEYRING_RESULT_CANNOT_CREATE_KEYRING,
	        GNOME_KEYRING_RESULT_ERROR_UNKNOWN
	};


       public GKEngine()
       {

       }

       ~GKEngine()
       {
           GnomeKeyring.ReleaseGnomeKeyringLib();
       }

       public XmlNode Aggregate()
       {
            XmlDocument doc = new XmlDocument();
           
            XmlNode rootElem = doc.CreateElement(ConstStrings.CCF_GKTAG);
            doc.AppendChild(rootElem);
            XmlElement keyringElem;

            ArrayList itemList;
            ArrayList attrList;
            ItemInfo itemInfo;
            KeyringInfo keyringInfo;
            int itemId;
            ArrayList keyringList = GnomeKeyring.GKGetKeyrings();
            IEnumerator kEtor = keyringList.GetEnumerator();
            IEnumerator iEtor;
           
            while(kEtor.MoveNext())
            {
                string keyring = (string)(kEtor.Current);

                keyringElem = doc.CreateElement(ConstStrings.CCF_GKKEYRING);
                XmlAttribute idAttr = doc.CreateAttribute(ConstStrings.CCF_ID);
                idAttr.Value = keyring;
                keyringElem.SetAttributeNode(idAttr);

                keyringInfo = GnomeKeyring.GKGetKeyringInfo(keyring);

                itemList = GnomeKeyring.GKGetItems(keyring);
                iEtor = itemList.GetEnumerator();
                while(iEtor.MoveNext())
                {
                    itemId = (int)iEtor.Current;
                    itemInfo = GnomeKeyring.GKGetItemInfo(keyring,itemId);
                    attrList = GnomeKeyring.GKGetAttributeList(keyring,itemId);

                    XmlElement secretElem = doc.CreateElement(ConstStrings.CCF_SECRET);
                    XmlAttribute secIdAttr = doc.CreateAttribute(ConstStrings.CCF_ID);
                    secIdAttr.Value = itemInfo.displayName + ":" + itemId;
                   
                    secretElem.SetAttributeNode(secIdAttr);

                    XmlAttribute typeAttr = doc.CreateAttribute(ConstStrings.CCF_TYPE);
                    typeAttr.Value = itemInfo.itemType.ToString();
                    secretElem.SetAttributeNode(typeAttr);
		    
                    XmlElement keyElem = null;
                    XmlAttribute keyIdAttr = null;
                    XmlElement valueElem = null;

                    if ((itemInfo.secret != null) && (itemInfo.secret.Length != 0))
                    {
                    	keyElem = doc.CreateElement(ConstStrings.CCF_KEY);
            	        keyIdAttr = doc.CreateAttribute(ConstStrings.CCF_ID);
                	keyIdAttr.Value = "password";
                    	keyElem.SetAttributeNode(keyIdAttr);

	                valueElem = doc.CreateElement(ConstStrings.CCF_VALUE);
        	        valueElem.InnerText = itemInfo.secret;
                        keyElem.AppendChild(valueElem);
                    	secretElem.AppendChild(keyElem);
                    }
                    
                    IEnumerator attrEtor = (IEnumerator)(attrList.GetEnumerator());
                    while(attrEtor.MoveNext())
                    {
                        Novell.CASA.DataEngines.GK.Attribute attr = (Novell.CASA.DataEngines.GK.Attribute)(attrEtor.Current);
                        keyElem = doc.CreateElement(ConstStrings.CCF_KEY);
                        keyIdAttr = doc.CreateAttribute(ConstStrings.CCF_ID);
                        keyIdAttr.Value = attr.key;
                        keyElem.SetAttributeNode(keyIdAttr);

                        valueElem = doc.CreateElement(ConstStrings.CCF_VALUE);
                        valueElem.InnerText = attr.value;
                        keyElem.AppendChild(valueElem);
                        secretElem.AppendChild(keyElem);
                    }
                    
                    keyringElem.AppendChild(secretElem);
                    XmlElement timeElem = doc.CreateElement(ConstStrings.CCF_TIME);

                    XmlElement itemCreatedTimeElem = doc.CreateElement(ConstStrings.CCF_CRTIME);
                    itemCreatedTimeElem.InnerText = itemInfo.mTime.ToString();
                    timeElem.AppendChild(itemCreatedTimeElem);

                    XmlElement itemModifiedTimeElem = doc.CreateElement(ConstStrings.CCF_MDTIME);
                    itemModifiedTimeElem.InnerText = itemInfo.cTime.ToString();
                    timeElem.AppendChild(itemModifiedTimeElem);

                    secretElem.AppendChild(timeElem);
                }
                XmlElement keyringTimeElem = doc.CreateElement(ConstStrings.CCF_TIME);

                XmlElement createdTimeElem = doc.CreateElement(ConstStrings.CCF_CRTIME);
                createdTimeElem.InnerText = keyringInfo.mTime.ToString();
                keyringTimeElem.AppendChild(createdTimeElem);

                XmlElement modifiedTimeElem = doc.CreateElement(ConstStrings.CCF_MDTIME);
                modifiedTimeElem.InnerText = keyringInfo.cTime.ToString();
                keyringTimeElem.AppendChild(modifiedTimeElem);

                keyringElem.AppendChild(keyringTimeElem);

                XmlElement lockElem = doc.CreateElement(ConstStrings.CCF_LOCK);

                XmlAttribute lockStatusAttr = doc.CreateAttribute(ConstStrings.CCF_LOCKSTATUS);
                if( keyringInfo.isLocked == 1 )
                    lockStatusAttr.Value = "locked";
                else
                    lockStatusAttr.Value = "unlocked";
                lockElem.SetAttributeNode(lockStatusAttr);

                XmlAttribute lockOnIdleAttr = doc.CreateAttribute(ConstStrings.CCF_LOCKHAND);
                if( keyringInfo.lockOnIdle == 1)
                    lockOnIdleAttr.Value = "true";
                else
                    lockOnIdleAttr.Value = "false";
                lockElem.SetAttributeNode(lockOnIdleAttr);

                XmlAttribute lockTimeoutAttr = doc.CreateAttribute(ConstStrings.CCF_LOCKTIME);
                lockTimeoutAttr.Value = keyringInfo.lockTimeout.ToString();
                lockElem.SetAttributeNode(lockTimeoutAttr);

                keyringElem.AppendChild(lockElem);
                rootElem.AppendChild(keyringElem);
            }
#if TEST
            XmlTextWriter writer = new XmlTextWriter("./gk.xml",null);
            writer.Formatting = Formatting.Indented;
            doc.Save(writer);
            writer.Close();
#endif

            return doc.ChildNodes[0];
        }

	public int SetSecret(XmlNode secret)
        {
		return (int)KeyringResultExtended.GNOME_KEYRING_RESULT_ERROR_UNKNOWN;
        }


         public  int SetSecret(XmlNode secret, int opnType)
         {
	   string password = "";         
           int retValue;

           try
	   {  
             int itemid = ExtractSecretId(secret); 
             string keyringname =  ExtractKeyringName(secret);
	    
	     NameValueCollection newNVC = new System.Collections.Specialized.NameValueCollection(); 
                                                                                                                                                             
             XmlNodeList keylist = secret.SelectNodes("descendant::Key");
             foreach (XmlNode tuple in keylist)
             {
                   //Get the Key
                   XmlAttributeCollection at = tuple.Attributes;
                   String keyname = (at["ID"]).InnerText;
                   if (keyname.Equals("password"))
                   {
			password =  tuple.ChildNodes[0].InnerText;

                   }
                   else
                   {
                   	newNVC.Add(keyname, tuple.ChildNodes[0].InnerText);
                   }
             }
            if (opnType == ConstStrings.OPERATION_ADD_SECRET ) //Add Item Opn
            {
	
                string strItemType = ExtractItemType(secret);
                string secretName = ExtractSecretName(secret, opnType);
	        
		return(GnomeKeyring.CreateSecret(keyringname, strItemType, secretName, password,  newNVC));
            }

            //Modify secret Opn
 	    retValue = GnomeKeyring.SetPassword(keyringname, itemid, password);

            if (retValue != 0)
            {
	
			return retValue;
	    }
	
            if (newNVC.Count != 0)
            {
	
            	return (GnomeKeyring.SetAttributes( keyringname, itemid, newNVC));
            }

            return 0;
           }
           catch(NullReferenceException n)
           {
                //Console.WriteLine("Exception in SetSecret = "+n.ToString());
		return (int)KeyringResultExtended.GNOME_KEYRING_RESULT_MALFORMED_XML;
           }
           catch(Exception e)
           {
                //Console.WriteLine("Exception in SetSecret = "+e.ToString());
 		return (int)KeyringResultExtended.GNOME_KEYRING_RESULT_ERROR_UNKNOWN;
           }
        }                                                                                                      

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

        public  int Remove(XmlNode secret)
        {
              try
              {
	              int itemid = ExtractSecretId(secret);
        	      string keyringname = ExtractKeyringName(secret);
              	      return (GnomeKeyring.RemoveItem( keyringname, itemid)); 
              }
              catch(NullReferenceException n)
              {
                	//Console.WriteLine("Exception in Remove = "+n.ToString());
			return (int)KeyringResultExtended.GNOME_KEYRING_RESULT_MALFORMED_XML;
              }
              catch(Exception e)
              {

			//Console.WriteLine("Exception in Remove = "+e.ToString());
        	        return (int)KeyringResultExtended.GNOME_KEYRING_RESULT_ERROR_UNKNOWN;
              }
        }

	public static Boolean IsStoreAvailable()
        {
              try
              {
		        System.Runtime.InteropServices.Marshal.PrelinkAll(typeof(GnomeKeyring));
                if (GnomeKeyring.IsGnomeKeyringInstalled())
                {
                    //Console.WriteLine("IsGnomeKeyringInstalled is true");
                    return true;
                }
                else
                {
                    //Console.WriteLine("IsGnomeKeyringInstalled is false");
                    return false;
                }
              }
              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;
              }
        }

          
        int ExtractSecretId(XmlNode secret)
        {
            XmlAttributeCollection atcol =  secret.Attributes;
	    String secretid = atcol["ID"].InnerXml;
            //Check if itemId is present
            /*if (secretid.EndsWith(":"))
            {
		return -2;
            }
            */
            int itemIdx = secretid.LastIndexOf(":");
            if (itemIdx == -1)
            {
               return -1;
            }
            int itemid = Convert.ToInt32(secretid.Substring(itemIdx + 1));
            return itemid;
        }
       
        string ExtractSecretName(XmlNode secret, int opnType)
        {
            XmlAttributeCollection atcol =  secret.Attributes;
	    String secretid = atcol["ID"].InnerXml;
            
            if (opnType == ConstStrings.OPERATION_ADD_SECRET)
            {
		return secretid;  //Not expecting an item Id
	
	    }
            int itemIdx = secretid.LastIndexOf(":");
            //Return substring without itemId 
            return(secretid.Substring(0,itemIdx));
        }
 
        string ExtractKeyringName(XmlNode secret)
        {
            XmlAttributeCollection atcol;
            XmlNode parentNode = secret.ParentNode;
            atcol =  parentNode.Attributes;
            String keyringname = atcol["ID"].InnerXml;
            return keyringname;
        }

          
        string ExtractItemType(XmlNode secret)
        {
	    XmlAttributeCollection atcol =  secret.Attributes;
            String itemType = atcol[ConstStrings.CCF_TYPE].InnerXml;
            return(itemType);
        }

         
        
#if TEST
    public static void Main()
    { 


                 GKEngine gk = new GKEngine();


                                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. Is Store Avail       *");
                                Console.WriteLine("* 7. Quit                 *");
                                Console.WriteLine("***************************");
                                Console.WriteLine("For all options the input is the file /root/gktest.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('7'))
                                                        return;
                                                if (c[0].Equals('6'))
                                                {
                                                        Console.WriteLine("Store Available is = " + GKEngine.IsStoreAvailable());
                                                      return;
                                                }
                                                if (c[0].Equals('5'))
                                                {
                                                         XmlNode node = gk.Aggregate ();
                                                         XmlDocument doc = node.OwnerDocument;
                                                         XmlTextWriter writer = new XmlTextWriter("/root/gktest.xml",null);
						         writer.Formatting = Formatting.Indented;
						         doc.Save(writer);
						         writer.Close();
                                                }
                                                else
                                                {

                                                        XmlDocument xmlDoc  = new XmlDocument();
                                                        XmlTextReader tr = new XmlTextReader("/root/gktest.xml");
                                                        tr.Read();
                                                        xmlDoc.Load(tr);
                                                        XmlNode root =  xmlDoc.LastChild;
                                                        if (root == null)
                                                        {
                                                                Console.WriteLine("Root is null");
                                                         }

							XmlNode secret  = root.ChildNodes[0].ChildNodes[0];
                                                        Console.WriteLine("secret Name \n" + secret.Name);
                                                if (c[0].Equals('4'))
                                                        res =gk.Remove(secret);
                                                else if (c[0].Equals('1'))
                                                        res = gk.SetSecret(secret, ConstStrings.OPERATION_ADD_SECRET);
                                                else if (c[0].Equals('2'))
                                                        res = gk.SetSecret(secret, ConstStrings.OPERATION_MODIFY_SECRET);
                                                else if (c[0].Equals('3'))
                                                        res = gk.SetSecret(secret);

                                              }
                                        }
                                }
                                Console.WriteLine("Result of Operation = " +  res);

        }
#endif

    }
}