/***********************************************************************
 * 
 *  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.FF;


namespace Novell.CASA.DataEngines
{

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

    class FFEngine : DataEngine
    {
	private static string XML_FIRFOXSECRET_TYPE_SIGNON = "Signon";		

	private String defaultProfileName=null;
	private int initProfileStatus=-1;  
   
	enum FireFoxResultExtended {
	        FIREFOX_RESULT_SUCESS = 1,
	        FIREFOX_RESULT_ERROR_UNKNOWN = 128
	};

       public FFEngine()
       {
 		//Get Profile Names
 		//Console.WriteLine("FFEngine Aggregate:Invoking GetDefaultProfileName:");
           	defaultProfileName=FireFox.GetDefaultProfileName(); 
		//This can be extended to get all profiles : Use FireFox.GetAllProfileNames()
		//Currently we support only the default profile.

		if(defaultProfileName!=null)
		{
			//Console.WriteLine("FFEngine Constructor:Initializing the Profile "+defaultProfileName);
			initProfileStatus=FireFox.InitProfile(defaultProfileName);
			//Console.WriteLine("FFEngine Constructor:InitProfile returned "+initProfileStatus);
		}
      }

       ~FFEngine()
       {
		//Uninitialize the profile if initialized earlier
		//FIXME:- CURRENTLY NOT IVOKED SINCE THERES IS A BUG WHICH NEEDS TO BE FIXED IN FF NATIVE		
		//Console.WriteLine("FFEngine Destructor:UnInitializing the Profile "+defaultProfileName);
		if(defaultProfileName!=null)
			FireFox.UninitProfile(defaultProfileName);
       }

       public XmlNode Aggregate()
       {

		//Init XML Document
		XmlDocument doc = new XmlDocument();           
            	XmlNode rootElem = doc.CreateElement(ConstStrings.CCF_FFTAG);
            	doc.AppendChild(rootElem);

		//Initialize & Aggregate DefaultProfile from Firefox
		if(defaultProfileName == null)
		{
			defaultProfileName=FireFox.GetDefaultProfileName(); 
			initProfileStatus=FireFox.InitProfile(defaultProfileName);
			if(initProfileStatus!=1)
				return rootElem;
		}
		
		/*Console.WriteLine("FFEngine.cs : Printing Doc to Console-BEFORE\n");
		doc.Save(Console.Out);
		int temp = readProfileToCCF(doc,rootElem,defaultProfileName);
		Console.WriteLine("FFEngine.cs : Printing Doc to Console-AFTER\n");
		doc.Save(Console.Out);*/

		int temp = readProfileToCCF(doc,rootElem,defaultProfileName);
	
		if(temp!=1)
		{
			//Console.WriteLine("FFEngine : Host List is null even when secrets are present");
			FireFox.UninitProfile(defaultProfileName);
			defaultProfileName = FireFox.GetDefaultProfileName();
			//Console.WriteLine("FFEngine : Default Profile Name : " + defaultProfileName);
			if(defaultProfileName==null)
				return rootElem;
			initProfileStatus = FireFox.InitProfile(defaultProfileName);
			if(initProfileStatus!=1)
				return rootElem;
			readProfileToCCF(doc,rootElem,defaultProfileName);
		}
		
		//Initialize & Aggregate any other profiles here if required....
	
 		//Console.WriteLine("FFEngine:Returning Aggregated Node for FireFox:");
		return rootElem;

        }



	public int SetSecret(XmlNode secret)
        {
		return (int)FireFoxResultExtended.FIREFOX_RESULT_ERROR_UNKNOWN;
        }


         public  int SetSecret(XmlNode secret, int opnType)
         {
		string ProfileName=null,secretName=null;
		int retVal=0;
		ProfileName = ExtractProfileName(secret);
		//Console.WriteLine("FfEngine.cs : ProfileName : " + ProfileName);
		secretName = ExtractSecretName(secret, opnType);
		
		Host newHost = new Host();
		HostElement nh1 = null;
		try
		{
			newHost.hostName = Marshal.StringToHGlobalAnsi(secretName);
			//Console.WriteLine("FFEngine.cs : SecretName " + secretName);
		}catch(Exception e)
		{
			Console.WriteLine("Unable to Marshal the SecretName" + e.ToString());
		}
		XmlNodeList keylist = secret.SelectNodes("descendant::Key");
		try 
		{
			IntPtr next = IntPtr.Zero;

			for (int i=keylist.Count-1;i>=0;i--)
             		{
                   		//Get the Key
		   		HostElement nh = new HostElement();
		                XmlAttributeCollection at = keylist.Item(i).Attributes;
                   		String keyname = (at["ID"]).InnerText;
		   		String passwordstatus = (at["PasswordStatus"]).InnerText;
		   		//Console.WriteLine("FFEngine.cs : Keyname : " + keyname);
		   		//Console.WriteLine("FFEngine.cs : Value : " + keylist.Item(i).ChildNodes[0].InnerText );	
                   		nh.name = Marshal.StringToHGlobalAnsi(keyname);
		   		nh.value = Marshal.StringToHGlobalAnsi(keylist.Item(i).ChildNodes[0].InnerText);
		   		nh.isPassword = Convert.ToInt32(passwordstatus);
		     		nh.next = next;
		   		next = Marshal.AllocHGlobal(Marshal.SizeOf(nh));
		   		Marshal.StructureToPtr(nh,next,false);	
	     		}
		   	newHost.hostElement = next;

		 	retVal = FireFox.Modify_Host(ProfileName,newHost,1);
		}
		catch(Exception e)
		{
			Console.WriteLine("Unable to Marshal the Key/Value Pairs" + e.ToString());
		}
		return retVal;
	 }         
        
        public  int GetSecret(XmlNode secret)
        {
            return ConstStrings.CASA_SUCCESS;
        }

        public  int Remove(XmlNode secret)
        {
		string ProfileName = ExtractProfileName(secret);
		string secretName = ExtractSecretName(secret, ConstStrings.OPERATION_DELETE_SECRET);
		int retVal = FireFox.Remove_Host(ProfileName,secretName);
		//return (int)FireFoxResultExtended.FIREFOX_RESULT_ERROR_UNKNOWN;
		return retVal;
	}

 	//--------------------------------------------------------------
	//isMasterPasswordSet
	//Is MasterPassword Set For the specified (Currently-Default) profile
	//@param   
	//		None ; For supporting multiple profiles, 
	//		       this needs to be extended to pass profile names 
	//
	//	@return 1    if master password is set
	//        	0    if master password not set
	//--------------------------------------------------------------
        public  int isMasterPasswordSet()
        {
		return (int)FireFox.isMasterPasswordSetFor(defaultProfileName);
	}

 	//--------------------------------------------------------------
	//verifyMasterPassword
	//Check if the specified master password is correct or not for the (Currently-Default) profile. 
	//If it is correct then password is stored to the internal store for later use. 
	//If it is wrong then nothing is stored and 0 will be returned.
	//
	//@param   
	//	masterPassword (string)
	//	; For supporting multiple profiles, this needs to be extended to pass profile names 
	//
	//	@return  1   if the specified master password is correct
        //		 0   if the master password is wrong.
	//--------------------------------------------------------------
       public  int verifyMasterPassword(string masterPassword)
        {
		return (int)FireFox.checkMasterPassword(defaultProfileName,masterPassword);
	}


	//=================Local Methods====================================

	//--------------------------------------------------------------
	//readProfileToCCF
	//@param   
	//		doc 		(XmlDocument)	XML CCF Document for Firefox
	//		profileName 	(string)   	name of the profile to be read
	//@return  1     on success
	//         <=0   on error 
	//  <FireFox>
	//   <Profile ID="Default">
	//	<Secret ID="hostname url" Type="Signon">
	//		<Key ID="UserName">
	//			<value>user1</value>
	//		</Key>
	//		<Key ID="Password">
	//			<value>password value</value>
	//		</Key>
	//	</Secret>
	//    </Profile>
	//   </FireFox>
	//--------------------------------------------------------------
         public  int readProfileToCCF(XmlDocument doc,XmlNode rootElement,string profileName)
         {
		int methodStatusCode=-1; 
 
		if((profileName!=null)	&& (initProfileStatus==(int)FireFoxResultExtended.FIREFOX_RESULT_SUCESS))
		{
             		Host hostList = null;

 			//Console.WriteLine("FireFox:Getting Data for profile "+profileName);
			hostList = FireFox.GetProfileData(profileName);		
				
			if(hostList!=null)
			{// Something to Aggregate
					//Console.WriteLine("\nFFEngine.cs : HostList is not null");
		            		XmlElement xmlProfileElement;

              				String hostName;
					String name;
					String value;
					int  isPassword;

						methodStatusCode=1;
                 				xmlProfileElement = doc.CreateElement(ConstStrings.CCF_FFPROFILE);	//<Profile>
                 				XmlAttribute idAttr = doc.CreateAttribute(ConstStrings.CCF_ID);		//<Profile>-ID
                				idAttr.Value = profileName;
                				xmlProfileElement.SetAttributeNode(idAttr);

           					while (hostList != null)
	     					{
             						HostElement hostElementList;


                    					XmlElement xmlSecretElement = doc.CreateElement(ConstStrings.CCF_SECRET); //<Secret>


							hostName = (String)Marshal.PtrToStringAnsi(hostList.hostName);
               						//Console.WriteLine("--------------------------------------------");
               						//Console.WriteLine("FFEngine::hostName="+hostName);
                    					XmlAttribute secIdAttr = doc.CreateAttribute(ConstStrings.CCF_ID);	//<Secret>-ID
                    					secIdAttr.Value = hostName;                   
                    					xmlSecretElement.SetAttributeNode(secIdAttr);

                    					XmlAttribute typeAttr = doc.CreateAttribute(ConstStrings.CCF_TYPE);	//<Secret>-Type
                    					typeAttr.Value = XML_FIRFOXSECRET_TYPE_SIGNON;
                    					xmlSecretElement.SetAttributeNode(typeAttr);


							//hostElementList = new HostElement();
        	       					hostElementList = (HostElement)Marshal.PtrToStructure(hostList.hostElement, typeof(HostElement));
             						while (hostElementList != null)
	     						{

                    						XmlElement xmlKeyElement = null;
                    						XmlAttribute keyIdAttr = null;
								XmlAttribute keyPasswdStatusAttr = null;
                    						XmlElement xmlValueElement = null;

								name = (String)Marshal.PtrToStringAnsi(hostElementList.name);
               							//Console.WriteLine("FFEngine::name="+name);
								value = (String)Marshal.PtrToStringAnsi(hostElementList.value);
               							//Console.WriteLine("FFEngine::value="+value);
								isPassword = hostElementList.isPassword;
               							//Console.WriteLine("FFEngine::isPassword="+isPassword);

                    						xmlKeyElement = doc.CreateElement(ConstStrings.CCF_KEY);	//<Key
            	        					keyIdAttr = doc.CreateAttribute(ConstStrings.CCF_ID);		//<Key>-ID
                						keyIdAttr.Value = name;
                    						xmlKeyElement.SetAttributeNode(keyIdAttr);

								keyPasswdStatusAttr = doc.CreateAttribute(ConstStrings.CCF_PASSWDSTATUS);		//<Key>-ID
								if(isPassword == 1)
	                						keyPasswdStatusAttr.Value = "1";
								else
									keyPasswdStatusAttr.Value = "0";
                    						xmlKeyElement.SetAttributeNode(keyPasswdStatusAttr);

	                					xmlValueElement = doc.CreateElement(ConstStrings.CCF_VALUE);	//<value>
        	        					xmlValueElement.InnerText = value;
                        					xmlKeyElement.AppendChild(xmlValueElement);			//</value>
                    						xmlSecretElement.AppendChild(xmlKeyElement);			//</Key

								//INNERLOOP-Loop for hostElementList
        	       						if (hostElementList.next == IntPtr.Zero)
		       						{
	        	  						break;
		       						}

        	       						hostElementList = (HostElement)Marshal.PtrToStructure(hostElementList.next, typeof(HostElement));
							}

                    					xmlProfileElement.AppendChild(xmlSecretElement);			//</Secret>

							//OUTERLOOP-Loop for hostList
        	       					if (hostList.next == IntPtr.Zero)
		       					{
		         					//Console.WriteLine("FFEngine::End of all Data Items##");
	        	  					break;
		       					}

        	       					hostList = (Host)Marshal.PtrToStructure(hostList.next, typeof(Host));
						}

						rootElement.AppendChild(xmlProfileElement);					//</Profile>


			}

		}//~Aggregate profileName

		return methodStatusCode;
	 }



	public static Boolean IsStoreAvailable()
        {
              try
              {
		    if(FireFox.IsStoreAvailable() == 1)
                    	return true;                
		    else
			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;
              }
        }

	string ExtractSecretName(XmlNode secret, int opnType)
        {
            XmlAttributeCollection atcol =  secret.Attributes;
	    String secretid = atcol["ID"].InnerXml;
	    //Console.WriteLine("FFEngine.cs: SecretId : " + secretid);
            
            if (opnType == ConstStrings.OPERATION_ADD_SECRET)
            {
		return secretid;  //Not expecting an item Id
	
	    }

	    return secretid;
            //int itemIdx = secretid.LastIndexOf("]");
            //Return substring without itemId 
            //return(secretid.Substring(0,itemIdx));
        }
 
        string ExtractProfileName(XmlNode secret)
        {
            XmlAttributeCollection atcol;
            XmlNode parentNode = secret.ParentNode;
            atcol =  parentNode.Attributes;
            String profilename = atcol["ID"].InnerXml;
            return profilename;
        }

//#if TEST
    public static void Main()
    { 


                 FFEngine ff = new FFEngine();


                                Console.WriteLine();
                                Console.WriteLine("********** Menu ***********");
                                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/fftest.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 = " + FFEngine.IsStoreAvailable());
                                                      return;
                                                }
                                                if (c[0].Equals('5'))
                                                {
  							Console.WriteLine("FFEngine:Main:Aggregating:");
                                                        XmlNode node = ff.Aggregate ();
                                                        XmlDocument doc = node.OwnerDocument;
                                                        XmlTextWriter writer = new XmlTextWriter("/root/fftest.xml",null);
						        writer.Formatting = Formatting.Indented;
						        doc.Save(writer);
						        writer.Close();
                                                }
                                                else
                                                {

                                                        XmlDocument xmlDoc  = new XmlDocument();
                                                        XmlTextReader tr = new XmlTextReader("/root/fftest.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);
                                              }
                                        }
                                }
                                Console.WriteLine("Result of Operation = " +  res);

        }
//#endif

    }
}