/***********************************************************************
 * 
 *  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.Collections.Specialized;
using System.Text;
using System.Threading;
using sscs.verbs;
using sscs.cache;
using sscs.common;
using sscs.constants;

using System.Runtime.Serialization.Formatters.Binary;
using System.IO;

using Novell.CASA.MiCasa.Common;
using Novell.CASA.MiCasa.Communication;

namespace sscs.verbs 
{
    
	/*	 
	 * There will be one instance existing for every call made by the client.  
	 */

	internal class ObjectSerialization : SSVerb
	{

		private ushort msgId = 0;
		private uint inMsgLen  = 0;
        
		private byte[] inBuf;
		private byte[] outBuf;

		/*
		* This method sets the class member with the byte array received.
		*/
 
		public void SetMessageContent(byte[] ipcBytes)
		{
			CSSSLogger.ExecutionTrace(this);
			inBuf = ipcBytes;                   
		}

		/*
		 * This method does the actual implementation of ReadSecret 
		 *   
		 */

		public byte[] ProcessRequest(UserIdentifier userId)
		{									
			CSSSLogger.ExecutionTrace(this); 

			/* If an exception occurs in message format decoding,
			 * it is handled by AppHandler
			 */

			// Message Format decipher - Start
			msgId          = BitConverter.ToUInt16(inBuf,0);
			inMsgLen       = BitConverter.ToUInt32(inBuf,2);

			// check inMsgLen
			if ((inMsgLen < 6) || (inMsgLen > 65535))
			{
				throw new FormatException(" MsgLen invalid.");
			}								

			// deserialize the data
			BinaryFormatter formatter = new BinaryFormatter();
			MemoryStream ms = new MemoryStream(inBuf, 6, (int)inMsgLen - 6);						
			
			WrappedObject request;			
			WrappedObject reply;
			try 
			{
				request = (WrappedObject)formatter.Deserialize(ms);
				reply = ProcessMessage(request, userId);				
			}
			catch (Exception e)
			{
				reply = new WrappedObject(-1, null);				
			}

			// Serialize the WrappedObject and send the reply
			ms = new MemoryStream();
			formatter.Serialize(ms, reply);             

			int msLen = (int)ms.Length;
			outBuf = new byte[4+msLen];
			byte[] t = new byte[10];

			t = BitConverter.GetBytes(ms.Length);
			Array.Copy(t,0,outBuf,0,4);
			Array.Copy(ms.GetBuffer(), 0, outBuf, 4, msLen);
			SessionManager.RemoveUserSession(userId, false);
			return outBuf;
		}


		internal WrappedObject ProcessMessage(WrappedObject wo, UserIdentifier userId)
		{
			
			SecretStore ssStore = SessionManager.CreateUserSession(userId);
			
			try
			{				
				int action = wo.GetAction();
				switch (action)
				{
					case MiCasaRequestReply.VERB_PING_MICASAD:
					{
						return DoPing(wo);  						
					}

					case MiCasaRequestReply.VERB_SET_LINKED_KEY:
					{
						return DoSetLinkedKey(ssStore, wo);						
					}

					case MiCasaRequestReply.VERB_REMOVE_LINKED_KEY:
					{
						return DoRemoveLinkedKey(ssStore, wo);						
					}

					case MiCasaRequestReply.VERB_GET_LINKED_KEYS:
					{
						return DoGetLinkedKeys(ssStore, wo);
					}

					case MiCasaRequestReply.VERB_CREATE_TEST_SECRETS:
					{						
						return DoCreateTestSecrets(ssStore, wo);
					}

					case MiCasaRequestReply.VERB_REMOVE_TEST_SECRETS:
					{						
						return DoRemoveTestSecrets(ssStore, wo);
					}

					case MiCasaRequestReply.VERB_DUMP_LINKED_KEYS:
					{
						return DoDumpLinkedKeys(ssStore, wo);
					}
					
					case MiCasaRequestReply.VERB_WRITE_KEY:
					{
						return DoWriteKey(ssStore, wo);
					}
					
					case MiCasaRequestReply.VERB_LOCK_STORE:
					{
						ssStore.LockStore();
						return wo;
					}

					case MiCasaRequestReply.VERB_UNLOCK_STORE:
					{
						return DoUnlockStore(ssStore, wo);
					}

					case MiCasaRequestReply.VERB_REMOVE_ALL_SECRETS:
					{						
						// stop persistence
						//ssStore.StopPersistence();

						// remove secrets
						return DoRemoveAllSecrets(ssStore, wo);
					}

					case MiCasaRequestReply.VERB_GET_STORE_STATUS:
					{		
						wo.SetObject(ssStore.GetSecretStoreState());
						return wo;
					}
					case MiCasaRequestReply.VERB_REMOVE_KEY:
					{
						return DoRemoveKey(ssStore, wo);
					}
					case MiCasaRequestReply.VERB_READ_KEY:
					{
						return DoReadKey(ssStore, wo);
					}
					case MiCasaRequestReply.VERB_GET_KEY_LIST:
					{
						return DoGetKeyList(ssStore, wo);
					}
					case MiCasaRequestReply.VERB_RESET_MASTER_PASSWORD:
					{
						return DoResetMasterPassword(ssStore, wo);
					}
					case MiCasaRequestReply.VERB_GET_SECRETIDS:
					{
						return DoGetSecretIDs(ssStore, wo);
					}

					default:
					{
						wo.SetError(constants.RetCodes.FAILURE, "Verb Not Supported");
						return wo;
					}
				}								
			}
			catch (Exception e)
			{
				wo.SetError(constants.RetCodes.FAILURE, e.ToString());				
			}
			
			
			return wo;
		}

		private WrappedObject DoGetSecretIDs(SecretStore ssStore, WrappedObject wo)
		{
			if (!ssStore.IsStoreLocked())
			{
				// look up keychain
				string sKeyChainID = wo.GetKeychainID();
				if (sKeyChainID != null)
				{	
					KeyChain kc = ssStore.GetKeyChain(sKeyChainID);
					if (kc != null)
					{
						StringCollection sc = (StringCollection)wo.GetObject();
						if (sc != null)
						{
							IDictionaryEnumerator etor = (IDictionaryEnumerator)kc.GetAllSecrets();
							while(etor.MoveNext())
							{
								string sID = (string)etor.Key;
								sID = sID.Substring(0, sID.Length - 1);
								sc.Add(UnescapeID(sID));
							}
						}
					}
					else
					{
						wo.SetError(constants.RetCodes.FAILURE, "KeyChain not found");
					}
				}
			}
			else
			{
				wo.SetError(constants.RetCodes.FAILURE, "Store locked");
			}

			return wo;

		}

		private WrappedObject DoRemoveAllSecrets(SecretStore ssStore, WrappedObject wo)
		{

			if (!ssStore.IsStoreLocked())
			{
				string sKeyChainID = wo.GetKeychainID();
				if (sKeyChainID != null)
				{
					KeyChain kc = ssStore.GetKeyChain(sKeyChainID);
					kc.RemoveAllSecrets();
					ssStore.UpdatePersistentStore();
				}
			}
			else
			{
				wo.SetError(constants.RetCodes.FAILURE, "Store locked");
			}
			
			return wo;
		}

		private WrappedObject DoUnlockStore(SecretStore ssStore, WrappedObject wo)
		{
			try 
			{
				string sMasterPassword = (string)wo.GetObject();
				ssStore.UnlockStore(null, sMasterPassword);
			}
			catch (Exception e)
			{
				wo.SetError(constants.RetCodes.FAILURE, e.ToString());
			}
			return wo;
		}
		private WrappedObject DoRemoveKey(SecretStore ssStore, WrappedObject wo)
		{
			try 
			{
				string keychainID = wo.GetKeychainID();
				string secretID = wo.GetSecretID();
				string keyID = wo.GetKeyID();

				KeyChain keyChain = ssStore.GetKeyChain(keychainID);
				Secret secret = null;		
				if( keyChain.CheckIfSecretExists(secretID) == false)
				{
					wo.SetError(constants.RetCodes.FAILURE,"Secret does not exist");
				}
				else
				{
					secret = keyChain.GetSecret(secretID);					
					secret.RemoveKeyValue(keyChain, keyID);			
					wo.SetError(constants.RetCodes.SUCCESS, null);	
					ssStore.UpdatePersistentStore();
				}
			}
			catch (Exception e)
			{
				wo.SetError(constants.RetCodes.FAILURE, e.ToString());
			}

			return wo;
		}

		private WrappedObject DoReadKey(SecretStore ssStore, WrappedObject wo)
		{
			if (!ssStore.IsStoreLocked())
			{
				try 
				{
					string keychainID = wo.GetKeychainID();
					string secretID = wo.GetSecretID();
					string keyID = wo.GetKeyID();


					//				string sValue = (String)wo.GetObject();

					KeyChain keyChain = ssStore.GetKeyChain(keychainID);
					Secret secret = null;		
					if( keyChain.CheckIfSecretExists(secretID) == false)
					{
						wo.SetError(constants.RetCodes.FAILURE,"Secret does not exist");
					}
					else
					{
						secret = keyChain.GetSecret(secretID);
						KeyValue kv = secret.GetKeyValue(keyID);			
						string value = kv.GetValue();
						wo.SetObject(value);
						wo.SetError(constants.RetCodes.SUCCESS, null);				
					}
				}
				catch (Exception e)
				{
					wo.SetError(constants.RetCodes.FAILURE, e.ToString());
				}
			}
			else
				wo.SetError(constants.RetCodes.FAILURE, "Store locked");

			return wo;
		}
		private WrappedObject DoGetKeyList(SecretStore ssStore, WrappedObject wo)
		{
			try 
			{
				string keychainID = wo.GetKeychainID();
				string secretID = wo.GetSecretID();



				KeyChain keyChain = ssStore.GetKeyChain(keychainID);
				Secret secret = null;		
				if( keyChain.CheckIfSecretExists(secretID) == false)
				{
					wo.SetError(constants.RetCodes.FAILURE,"Secret does not exist");
				}
				else
				{
					secret = keyChain.GetSecret(secretID);
					if( null != secret )
					{
						ArrayList keyList = secret.GetKeyList();
						wo.SetObject(keyList);
						wo.SetError(constants.RetCodes.SUCCESS, null);				
					}
				}
			}
			catch (Exception e)
			{
				wo.SetError(constants.RetCodes.FAILURE, e.ToString());
			}

			return wo;
		}

		private WrappedObject DoWriteKey(SecretStore ssStore, WrappedObject wo)
		{
			if (!ssStore.IsStoreLocked())
			{
				try 
				{
					string keychainID = wo.GetKeychainID();
					string secretID = wo.GetSecretID();
					string keyID = wo.GetKeyID();
					string sValue = (String)wo.GetObject();
							
					if (secretID.IndexOf("*") < 0)
					{

						KeyChain keyChain = ssStore.GetKeyChain(keychainID);
						Secret secret;		
						if( keyChain.CheckIfSecretExists(secretID) == false)
						{
							secret = new Secret(secretID);
							keyChain.AddSecret(secret);
						}
						else
						{
							secret = keyChain.GetSecret(secretID);
						}
						secret.SetKeyValue(keyID, sValue);			
                                 
						ChangeLinkedKeys(keyChain, secret, keyID, sValue);
						wo.SetError(constants.RetCodes.SUCCESS, null);				
						ssStore.UpdatePersistentStore();
					}
					else
						wo.SetError(constants.RetCodes.FAILURE, null);
				}
				catch (Exception e)
				{
					wo.SetError(constants.RetCodes.FAILURE, e.ToString());
				}				
				
			}
			else
				wo.SetError(constants.RetCodes.FAILURE, "Store locked");

			return wo;

		}

		private void ChangeLinkedKeys(KeyChain keyChain, Secret secret, string key, string valStr)
		{
			Hashtable htLinkedkeys = secret.GetLinkedKeys(key);
			if (htLinkedkeys != null)
			{
				// enumerate the hashtable, getting each secret/key and change it's value
				ICollection coll = htLinkedkeys.Values;
				IDictionaryEnumerator ienum = (IDictionaryEnumerator)coll.GetEnumerator();
				
				LinkedKeyInfo linkedInfo; // = (LinkedKeyInfo)ienum.Current;
											
				while (ienum.MoveNext())
				{
					linkedInfo = (LinkedKeyInfo)ienum.Value;
		
					// Get the target Secret
					Secret targetSecret = keyChain.GetSecret(linkedInfo.GetLinkedSecretID());
					if (targetSecret != null)
					{
						// get target key value
						string targetKey = linkedInfo.GetLinkedKeyID();
						string targetkv = targetSecret.GetKeyValue(targetKey).GetValue();
																	
						// if a change is required in the target, then call this method recursively using the TargetSecret
						if (!targetkv.Equals(valStr))
						{
							// NOTE: ORDER IS IMPORTANT
							// first change this one
							targetSecret.SetKeyValue(linkedInfo.GetLinkedKeyID(), valStr);		

							// now call the traget to change it's linked ones
							ChangeLinkedKeys(keyChain, targetSecret, targetKey, valStr);
							
						}															
					}	
				}				
			}
		}

		private WrappedObject DoDumpLinkedKeys(SecretStore ssStore, WrappedObject wo)
		{
			// create 2 secrets
			string keychainID = wo.GetKeychainID();
			KeyChain keyChain;


			Console.WriteLine("\r\n*********Dumping Linked Keys");
			try
			{
				keyChain = ssStore.GetKeyChain(keychainID);
			}
			catch (Exception)
			{
				keyChain = new KeyChain(keychainID);
				ssStore.AddKeyChain(keyChain);
			}

			// enumerate all secrets
			IDictionaryEnumerator ienum = (IDictionaryEnumerator)keyChain.GetAllSecrets();
			ienum.Reset();
			while (ienum.MoveNext())
			{
				string secretID = (string)ienum.Key;
				Secret secret = keyChain.GetSecret(secretID);
				IDictionaryEnumerator idenum = secret.GetKeyValueEnumerator();
				idenum.Reset();
				while (idenum.MoveNext())
				{	
					KeyValue kv = (KeyValue)idenum.Value;										
					Hashtable htLKI = kv.GetLinkedKeys();
					
					if (htLKI != null)
					{
						Console.WriteLine(secret.GetKey() + ":" + kv.Key + " is Linked to");
						IDictionaryEnumerator htienum = (IDictionaryEnumerator)htLKI.GetEnumerator();
						htienum.Reset();
						while (htienum.MoveNext())
						{
							LinkedKeyInfo lki = (LinkedKeyInfo) htienum.Value;
							Console.WriteLine("      " + lki.GetLinkID());
						}
					}
				}				
			}
			return wo;

		}
		
	
		string GW		= "SS_CredSet:TestGroupwise\0";
		string IFOLDER  = "SS_CredSet:TestiFolder\0";
		string GWIM		= "SS_CredSet:TestGWIM\0";

		private WrappedObject DoRemoveTestSecrets(SecretStore ssStore, WrappedObject wo)
		{
			
			string keychainID = wo.GetKeychainID();
			KeyChain keyChain;

			try
			{
				keyChain = ssStore.GetKeyChain(keychainID);
			}
			catch (Exception)
			{
				return wo;
			}

			try
			{
				keyChain.RemoveSecret(GW);
			}
			catch (Exception)
			{}

			try
			{
				keyChain.RemoveSecret(GWIM);
			}
			catch (Exception)
			{}

			try
			{
				keyChain.RemoveSecret(IFOLDER);
			}
			catch (Exception)
			{}

			return wo;

		}

		private WrappedObject DoCreateTestSecrets(SecretStore ssStore, WrappedObject wo)
		{
			// create 2 secrets
			string keychainID = wo.GetKeychainID();
			KeyChain keyChain;

			try
			{
				keyChain = ssStore.GetKeyChain(keychainID);
			}
			catch (Exception)
			{
				keyChain = new KeyChain(keychainID);
				ssStore.AddKeyChain(keyChain);
			}
			
			Secret secret1 = GetOrCreateSecret(keyChain, GW);
			secret1.SetKeyValue("CN", "User1");
			secret1.SetKeyValue("Password", "GWPass");
			try 
			{			
				keyChain.AddSecret(secret1);
			}
			catch (Exception)
			{}

			Secret secret2 = GetOrCreateSecret(keyChain, IFOLDER);
			secret2.SetKeyValue("CN", "User2");
			secret2.SetKeyValue("Password", "IfolderPass");
			secret2.SetKeyValue("EmployeeID", "123456");
			try 
			{			
				keyChain.AddSecret(secret2);
			}
			catch (Exception)
			{}

			Secret secret3 = GetOrCreateSecret(keyChain,GWIM);
			secret3.SetKeyValue("CN", "User3");
			secret3.SetKeyValue("Password", "GwimPass");
			try 
			{			
				keyChain.AddSecret(secret3);
			}
			catch (Exception)
			{}

			try
			{
				// link password fields
				KeyValue kv = secret1.GetKeyValue("Password");								
				KeyValue kv2 = secret2.GetKeyValue("Password");								
				KeyValue kv3 = secret3.GetKeyValue("Password");
				
				kv.AddLink(new LinkedKeyInfo(IFOLDER, "Password"));
				kv2.AddLink(new LinkedKeyInfo(GW, "Password"));

				kv2.AddLink(new LinkedKeyInfo(GWIM, "Password"));
				kv3.AddLink(new LinkedKeyInfo(IFOLDER, "Password"));

				kv3.AddLink(new LinkedKeyInfo(GW, "Password"));
				kv.AddLink(new LinkedKeyInfo(GWIM, "Password"));
			}
			catch (Exception e)
			{
				//Console.WriteLine(e.ToString());
			}

#if DEBUG
			DoDumpLinkedKeys(ssStore, wo);
#endif

			return wo;
		}

		private Secret GetOrCreateSecret(KeyChain kc, string sID)
		{
			try
			{
				return kc.GetSecret(sID);
			}
			catch (Exception)
			{
				return new Secret(sID);
			}
		}
		

		private WrappedObject DoGetLinkedKeys(SecretStore ssStore, WrappedObject wo)
		{
			string keychainID = wo.GetKeychainID();
			string secretID = wo.GetSecretID();
			string keyID = wo.GetKeyID();

			KeyChain keyChain = ssStore.GetKeyChain(keychainID);
			Secret secret = keyChain.GetSecret(secretID);		
			KeyValue kv = secret.GetKeyValue(keyID);

			wo.SetObject(kv.GetLinkedKeys());
			wo.SetError(constants.RetCodes.SUCCESS, null);
			return wo;
		}

		private WrappedObject DoRemoveLinkedKey(SecretStore ssStore, WrappedObject wo)
		{

			string keychainID = wo.GetKeychainID();
			string secretID = wo.GetSecretID();
			string keyID = wo.GetKeyID();

			KeyChain keyChain = ssStore.GetKeyChain(keychainID);
			Secret secret = keyChain.GetSecret(secretID);		
			KeyValue kv = secret.GetKeyValue(keyID);

			LinkedKeyInfo lki = (LinkedKeyInfo)wo.GetObject();
			kv.RemoveLink(lki.GetLinkID());

			// remove reverse link as well
			Secret targetSecret = keyChain.GetSecret(lki.GetLinkedSecretID());
			KeyValue targetkv = targetSecret.GetKeyValue(lki.GetLinkedKeyID());
			targetkv.RemoveLink(secretID+":"+keyID);
			ssStore.UpdatePersistentStore();

			return wo;
		}
			
		private WrappedObject DoSetLinkedKey(SecretStore ssStore, WrappedObject wo)
		{

			string keychainID = wo.GetKeychainID();
			string secretID = wo.GetSecretID();
			string keyID = wo.GetKeyID();

			KeyChain keyChain = ssStore.GetKeyChain(keychainID);
			Secret secret = keyChain.GetSecret(secretID);		
			KeyValue kv = secret.GetKeyValue(keyID);

			LinkedKeyInfo lki = (LinkedKeyInfo)wo.GetObject();
			kv.AddLink(lki);

			// add reverse link
			try
			{
				Secret target = keyChain.GetSecret(lki.GetLinkedSecretID());
				KeyValue targetkv = target.GetKeyValue(lki.GetLinkedKeyID());
				targetkv.AddLink(new LinkedKeyInfo(secretID, keyID, true));
				ssStore.UpdatePersistentStore();
			}
			catch (Exception e)
			{
				//Console.WriteLine("Reverse Link error: " + e.ToString());
				wo.SetError(constants.RetCodes.FAILURE, "Reverse Link: " + e.ToString());
			}

			return wo;
		}


		private WrappedObject DoResetMasterPassword(SecretStore ssStore, WrappedObject wo)
		{
			ResetMasterPassword rmp = (ResetMasterPassword)wo.GetObject();				
			// verify current master password
			try 
			{
				string sMasterPassword = rmp.m_currentPassword;
				ssStore.UnlockStore(null, sMasterPassword);
			}
			catch (Exception e)
			{
				wo.SetError(constants.RetCodes.FAILURE, e.ToString());
				return wo;
			}

			// change master master password
			string sNewPassword =  rmp.m_newPassword;
			if (sNewPassword == null || sNewPassword.Length < 8)
			{
				wo.SetError(constants.RetCodes.FAILURE, null);
				return wo;
			}

			if (!ssStore.ChangeMasterPassword(rmp.m_currentPassword, rmp.m_newPassword))
				wo.SetError(constants.RetCodes.FAILURE, null);	
			else
				wo.SetError(constants.RetCodes.SUCCESS, null);


			return wo;
		}
		
		private WrappedObject DoPing(WrappedObject wo)
		{
			//Console.WriteLine("MICASAD received Ping from Client");
			wo.SetError(IPCRetCodes.SSCS_REPLY_SUCCESS, null);													
					
			Ping ping = (Ping)wo.GetObject();
			ping.servermessage = "MICASAD echos client message: <" + ping.clientmessage + ">";
			return wo;
		}


		/*
							internal WrappedObject ProcessMessage(WrappedObject wo, UserIdentifier userId)
							{

								Console.WriteLine("ObjectSerialization Called");
								SecretStore ssStore = SessionManager.CreateUserSession(userId);
								Secret secret;

								try
								{
									keyChainId = wo.GetKeychainID();
									secretId = wo.GetSecretID();
									keyId = wo.GetKeyID();
				
									int action = wo.GetAction();

									if (action == WrappedObject.VERB_PING_MICASAD)
									{
										Console.WriteLine("MICASAD received Ping from Client");
										wo.SetError(IPCRetCodes.SSCS_REPLY_SUCCESS, null);													
					
										Ping ping = (Ping)wo.GetObject();
										ping.servermessage = "MICASAD echos client message: <" + ping.clientmessage + ">";
										return wo;
									}

									else if (action <= WrappedObject.VERB_REMOVE_LINKED_KEY)
									{

										wo.SetError(IPCRetCodes.SSCS_REPLY_SUCCESS, null);																	
										if (action == WrappedObject.VERB_GET_STORE)
										{
											wo.SetObject(ssStore);	
											return wo;
										}

										// look up Keychain
										KeyChain keyChain = new KeyChain(keyChainId);

										// temporary
										try
										{
											if (true)
												ssStore.AddKeyChain(keyChain);
										}
										catch (Exception e)
										{

										}
					
										if( ssStore.CheckIfKeyChainExists(keyChainId) )
										{
											keyChain = ssStore.GetKeyChain(keyChainId);
											if (action == WrappedObject.VERB_GET_KEYCHAIN)
											{
												wo.SetObject(keyChain);							
												return wo;
											}
					
											if((action == WrappedObject.VERB_GET_SECRET) && (keyChain.CheckIfSecretExists(secretId) == false))
											{
												wo.SetError(IPCRetCodes.SSCS_E_SECRETID_DOES_NOT_EXIST, null);
												return wo;
											}
											else
											{
												secret = keyChain.GetSecret(secretId);
						
												if (action == WrappedObject.VERB_GET_SECRET)
												{
													wo.SetObject(secret);								
													return wo;
												}
												else if ((action == WrappedObject.VERB_SET_SECRET) && (null != wo.GetObject()))
												{
													keyChain.AddSecret((Secret)wo.GetObject());
												}

												else if (action == WrappedObject.VERB_SET_LINKED_KEY)
												{
													KeyValue kv = secret.GetKeyValue(keyId);
													LinkedKeyInfo lki = (LinkedKeyInfo)wo.GetObject();
													kv.AddLink(lki);

													// add reverse link
													try
													{
														Secret target = keyChain.GetSecret(lki.GetLinkedSecretID());
														KeyValue targetkv = target.GetKeyValue(lki.GetLinkedKeyID());
														targetkv.AddLink(new LinkedKeyInfo(secretId, keyId));
													}
													catch (Exception e)
													{
														Console.WriteLine("Reverse Link error: " + e.ToString());
													}
												}
											}
										}
				
										else
										{
											wo.SetError(IPCRetCodes.SSCS_E_KEYCHAIN_DOES_NOT_EXIST, null);
										}						
					
																		
									}
									else
									{
										wo.SetError(IPCRetCodes.SSCS_E_SYSTEM_ERROR, "Action not supported");
									}

				
								}
								catch (Exception e)
								{
									String error = e.ToString();
									Console.WriteLine(error);
									wo = new WrappedObject(constants.RetCodes.FAILURE, error);
								}															
								return wo;
							}			
					*/

		private static string UnescapeID(string sOrig)
		{
			StringBuilder sb = new StringBuilder();
			for (int i = 0; i < sOrig.Length; i++)
			{
				if (sOrig[i] == ('\\'))
				{
					if (i + 1 < sOrig.Length)
					{
						if (sOrig[i + 1] == (':')
							|| sOrig[i + 1] == ('\\')
							|| sOrig[i + 1] == ('='))
						{
							sb.Append(sOrig[i + 1]);
							i++;
						} 
					}
					else
						sb.Append(sOrig[i]);
				}
				else
					sb.Append(sOrig[i]);
			}
			return sb.ToString();
		}
		public string GetVerbName()
		{
			CSSSLogger.ExecutionTrace(this);
			return this.ToString(); 
		}
						
	}
}