package com.isbc.smartcard.esmarttoken.applet;

import java.applet.Applet;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.security.*;
import java.util.ArrayList;
import java.util.List;

import javax.smartcardio.CardTerminal;
import javax.smartcardio.CardTerminals;

import com.isbc.smartcard.esmarttoken.api.ErrorCode;
import com.isbc.smartcard.esmarttoken.api.Pkcs11ObjectAttribute;
import com.isbc.smartcard.esmarttoken.api.Pkcs11Wrapper;
import com.isbc.smartcard.esmarttoken.utils.HexUtils;
import com.isbc.smartcard.esmarttoken.utils.TerminalUtils;

/**
 * Wrapper for PKCS#11 invocation via WEB
 * @author Sergey Panov 
 */
public class AppletWrapper extends Applet
{
	private static final long serialVersionUID = 1L;
	private static String moduleName;
	
	static
	{
		String os_name = System.getProperty("os.name").toLowerCase();
		if (os_name.indexOf("windows") > -1)
	    {
	    	moduleName = "isbc_pkcs11_main.dll";
	    }
		else if (os_name.indexOf("mac os") > -1)
	    {
			moduleName = "libisbc_pkcs11_main.dylib";
	    }
	    else
	    {
	    	moduleName = "libisbc_pkcs11_main.so";
	    }
	}
	
	@Override
	public void init()
	{
		// Nothing to init
	}
	
	/**
     * Clearing card by deleting all object from token
	 * @param pin
	 * 				User PIN code
     * @return
     * 				Result code for the operation.
     * 				Code 0 means success.
     * 				Positive values in according to PKCS #11 v2.20 specification error codes 
     * 				Negative values in according to ErrorCode class
     * @see ErrorCode
     */
	public String cleanCard(final String pin)
    {
    	return AccessController.doPrivileged(new PrivilegedAction<String>()
    	{
    		public String run()
    		{
				long res = -1;
    			try
    			{
    				Pkcs11Wrapper wrapper = new Pkcs11Wrapper();
    				res = wrapper.cleanCardWrap(null, moduleName, pin.getBytes());
    			}
    			catch (Throwable e)
    			{
    				 //e.printStackTrace();
    				 return "Error " + e.getMessage();
    			}
    			
    			return "Code " + res;
    		}
    	});
    }
	
	/**
     * Initialize token (C_InitToken from PKCS#11 API)
	 * @param pin
	 * 				User PIN code
     * @return
     * 				Result code for the operation.
     * 				Code 0 means success.
     * 				Positive values in according to PKCS #11 v2.20 specification error codes 
     * 				Negative values in according to ErrorCode class
     * @see ErrorCode
     */
	public String initCard(final String pin)
    {
    	return AccessController.doPrivileged(new PrivilegedAction<String>()
    	{
    		public String run()
    		{
				long res = -1;
    			try
    			{
    				Pkcs11Wrapper wrapper = new Pkcs11Wrapper();
    				res = wrapper.initCardWrap(null, moduleName, pin.getBytes());
    			}
    			catch (Throwable e)
    			{
    				 //e.printStackTrace();
    				 return "Error " + e.getMessage();
    			}
    			
    			return "Code " + res;
    		}
    	});
    }
	
	
	/**
     * Generate RSA key pair (C_GenerateKeyPair from PKCS#11 API)
	 * @param pin
	 * 				User PIN code
     * @return
     * 				Result code for the operation.
     * 				Code 0 means success.
     * 				Positive values in according to PKCS #11 v2.20 specification error codes 
     * 				Negative values in according to ErrorCode class
     * @see ErrorCode
     */
    public String generateKeyPair(final String pin)
    {
   
    	return AccessController.doPrivileged(new PrivilegedAction<String>()
    	{
    		public String run()
    		{
				long res = 0;
    			try
    			{
    				Pkcs11Wrapper wrapper = new Pkcs11Wrapper();
    				res = wrapper.generateKeyPairWrap(null, moduleName, pin.getBytes(), 1024, "applet1024key", HexUtils.decodeHex("00112233"));
    			}
    			catch (Throwable e)
    			{
    				 //e.printStackTrace();
    				return "Error " + e.getMessage();
    			}
    			
    			return "Return " + res;
    		}
    	});
    }
    
    /**
     * List token's objects
	 * @param pin
	 * 				User PIN code
     * @return
     * 				Result code for the operation and list objects.
     * 				Code 0 means success.
     * 				Positive values in according to PKCS #11 v2.20 specification error codes 
     * 				Negative values in according to ErrorCode class
     * @see ErrorCode
     */
    public String listObjects(final String pin)
    {
   
    	return AccessController.doPrivileged(new PrivilegedAction<String>()
    	{
    		public String run()
    		{
				long res = 0;
				StringBuilder bld = new StringBuilder();
    			try
    			{
    				Pkcs11Wrapper wrapper = new Pkcs11Wrapper();
    				List<Pkcs11ObjectAttribute> attrList = new ArrayList<Pkcs11ObjectAttribute>();
    				res = wrapper.listObjectsWrap(null, moduleName, pin.getBytes(), attrList);
    			    for (int i = 0; i < attrList.size(); ++i)
    			    {
    			    	Pkcs11ObjectAttribute obj = attrList.get(i);
    			    	bld.append("Type: " + obj.getType() + "\n");
    			    	bld.append("Label: " + obj.getLabel() + "\n");
    			    	bld.append("ID: " + obj.getId() + "\n");
    			    }
    			}
    			catch (Throwable e)
    			{
    				 //e.printStackTrace();
    				return "Error " + e.getMessage();
    			}
    			
    			return "Return " + res + "\n" + bld.toString();
    		}
    	});
    }
    
    /**
     * Set user PIN
	 * @param oldpin
	 * 				Old user PIN code
	 * @param newpin
	 * 				New user PIN code
     * @return
     * 				Result code for the operation and list objects.
     * 				Code 0 means success.
     * 				Positive values in according to PKCS #11 v2.20 specification error codes 
     * 				Negative values in according to ErrorCode class
     * @see ErrorCode
     */
    public String setPin(final String oldpin, final String newpin)
    {
   
    	return AccessController.doPrivileged(new PrivilegedAction<String>()
    	{
    		public String run()
    		{
				long res = 0;
				try
    			{
    				Pkcs11Wrapper wrapper = new Pkcs11Wrapper();
    				res = wrapper.setPinWrap(null, moduleName, oldpin.getBytes(), newpin.getBytes());
    			}
    			catch (Throwable e)
    			{
    				 //e.printStackTrace();
    				 return "Error " + e.getMessage();
    			}
    			
    			return "Return " + res;
    		}
    	});
    }
    
    /**
     * Set SO PIN
	 * @param oldpin
	 * 				Old SO PIN code
	 * @param newpin
	 * 				New SO PIN code
     * @return
     * 				Result code for the operation and list objects.
     * 				Code 0 means success.
     * 				Positive values in according to PKCS #11 v2.20 specification error codes 
     * 				Negative values in according to ErrorCode class
     * @see ErrorCode
     */
    public String setPuk(final String oldpin, final String newpin)
    {
   
    	return AccessController.doPrivileged(new PrivilegedAction<String>()
    	{
    		public String run()
    		{
				long res = 0;
				try
    			{
    				Pkcs11Wrapper wrapper = new Pkcs11Wrapper();
    				res = wrapper.setPukWrap(null, moduleName, oldpin.getBytes(), newpin.getBytes());
    			}
    			catch (Throwable e)
    			{
    				//e.printStackTrace();
   				 	return "Error " + e.getMessage();
    			}
    			
    			return "Return " + res;
    		}
    	});
    }
    
    /**
     * Load certificate from PKCS#12 file
	 * @param pin
	 * 				User PIN code
	 * @param filepath
	 * 				Path to the file with certificate content
	 * @param filepass
	 * 				Password for the loaded PKCS#12 file
     * @return
     * 				Result code for the operation and list objects.
     * 				Code 0 means success.
     * 				Positive values in according to PKCS #11 v2.20 specification error codes 
     * 				Negative values in according to ErrorCode class
     * @see ErrorCode
     */
    public String loadCert(final String pin, final String filepath, final String filepass)
    {
   
    	return AccessController.doPrivileged(new PrivilegedAction<String>()
    	{
    		public String run()
    		{
				long res = 0;
				try
    			{
    				Pkcs11Wrapper wrapper = new Pkcs11Wrapper();
    				
    				byte[] buffer = null;
					FileInputStream file = new FileInputStream(filepath);
			        int numberBytes = file.available();
			        buffer = new byte[numberBytes];
			        file.read(buffer);
			        file.close();
			        
			        res = wrapper.loadCertWrap(null, moduleName, buffer, filepass.getBytes(), pin.getBytes());
    			}
    			catch (Throwable e)
    			{
    				 //e.printStackTrace();
    				 return "Error " + e.getMessage();
    			}
    			
    			return "Return " + res;
    		}
    	});
    }
    
    /**
     * Get X509 certificate from the token
	 * @param pin
	 * 				User PIN code
	 * @param label
	 * 				Certificate object label
	 * @param id
	 * 				Certificate object id
	 * @param filepath
	 * 				File where certificate will be saved
     * @return
     * 				Result code for the operation and list objects.
     * 				Code 0 means success.
     * 				Positive values in according to PKCS #11 v2.20 specification error codes 
     * 				Negative values in according to ErrorCode class
     * @see ErrorCode
     */
    public String getX509Certificate(final String pin, final String label, final String id, final String filepath)
    {
   
    	return AccessController.doPrivileged(new PrivilegedAction<String>()
    	{
    		public String run()
    		{
				long res = 0;
				try
    			{
    				Pkcs11Wrapper wrapper = new Pkcs11Wrapper();
    				
    				List<Byte> buffer2 = new ArrayList<Byte>();
    			    res = wrapper.getX509CertificateWrap(null, moduleName, pin.getBytes(), label, 
    			    		HexUtils.decodeHex(id), buffer2);
    				if (!buffer2.isEmpty())
    				{
    					FileOutputStream file2 = new FileOutputStream(filepath);
						for (int i=0; i < buffer2.size(); ++i)
						{
							file2.write(buffer2.get(i).byteValue());
						}
						file2.close();
    				}
    			}
    			catch (Throwable e)
    			{
    				 //e.printStackTrace();
    				 return "Error " + e.getMessage();
    			}
    			
    			return "Return " + res;
    		}
    	});
    }
    
    /**
     * Get token UID
	 * @return
     * 				Result token UID or null
     */
    public String getUID()
    {
   
    	return AccessController.doPrivileged(new PrivilegedAction<String>()
    	{
    		public String run()
    		{
				String uid = "unknown";
    			try
    			{
    				CardTerminal terminal = TerminalUtils.getFirstValidTerminal();
    				uid = TerminalUtils.getCardUID(terminal);
    			}
    			catch (Throwable e)
    			{
    				 //e.printStackTrace();
    				 return "Error " + e.getMessage();
    			}
    			
    			return "Return " + uid;
    		}
    	});
    }
    
    /**
     * Get public key from the token
	 * @param pin
	 * 				User PIN code
	 * @param label
	 * 				Certificate object label
	 * @param id
	 * 				Certificate object id
	 * @param modulusfilepath
	 * 				File where public key module will be saved
 	 * @param exponentfilepath
	 * 				File where public key exponent will be saved
     * @return
     * 				Result code for the operation and list objects.
     * 				Code 0 means success.
     * 				Positive values in according to PKCS #11 v2.20 specification error codes 
     * 				Negative values in according to ErrorCode class
     * @see ErrorCode
     */
    public String getPublicKey(final String pin, final String label, final String id, final String modulusfilepath, final String exponentfilepath)
    {
   
    	return AccessController.doPrivileged(new PrivilegedAction<String>()
    	{
    		public String run()
    		{
				long res = 0;
				try
    			{
    				Pkcs11Wrapper wrapper = new Pkcs11Wrapper();
    				
    				List<Byte> modulusBuff = new ArrayList<Byte>();
    				List<Byte> exponenBuff = new ArrayList<Byte>();
    			    res = wrapper.getPublicKeyWrap(null, moduleName, pin.getBytes(), label, 
    			    		HexUtils.decodeHex(id), modulusBuff, exponenBuff);
    				if (!modulusBuff.isEmpty())
    				{
    					FileOutputStream file2 = new FileOutputStream(modulusfilepath);
						for (int i=0; i < modulusBuff.size(); ++i)
						{
							file2.write(modulusBuff.get(i).byteValue());
						}
						file2.close();
						
						file2 = new FileOutputStream(exponentfilepath);
						for (int i=0; i < exponenBuff.size(); ++i)
						{
							file2.write(exponenBuff.get(i).byteValue());
						}
						file2.close();
    				}
    			}
    			catch (Throwable e)
    			{
    				 //e.printStackTrace();
    				 return "Error " + e.getMessage();
    			}
    			
    			return "Return " + res;
    		}
    	});
    }
    
    /**
     * Load X509 certificate to token (C_ISBC_ImportX509Certificate from PKCS#11 API)
	 * @param pin
	 * 				User PIN code
	 * @param label
	 * 				Certificate object label
	 * @param id
	 * 				Certificate object id
	 * @param filepath
	 * 				Path to the file with certificate content
     * @return
     * 				Result code for the operation and list objects.
     * 				Code 0 means success.
     * 				Positive values in according to PKCS #11 v2.20 specification error codes 
     * 				Negative values in according to ErrorCode class
     * @see ErrorCode
     */
    public String loadX509Certificate(final String pin, final String label, final String id, final String filepath)
    {
   
    	return AccessController.doPrivileged(new PrivilegedAction<String>()
    	{
    		public String run()
    		{
				long res = 0;
				try
    			{
    				Pkcs11Wrapper wrapper = new Pkcs11Wrapper();
    				
    				byte[] buffer = null;
					FileInputStream file = new FileInputStream(filepath);
			        int numberBytes = file.available();
			        buffer = new byte[numberBytes];
			        file.read(buffer);
			        file.close();
    			    
			        res = wrapper.loadX509CertificateWrap(null, moduleName, pin.getBytes(), label, HexUtils.decodeHex(id), buffer);
    			}
    			catch (Throwable e)
    			{
    				 //e.printStackTrace();
    				 return "Error " + e.getMessage();
    			}
    			
    			return "Return " + res;
    		}
    	});
    }
    
    /**
     * Check if JNI library found and can be loaded
     * @return
     * 				Code 1 means success, 0 means unsuccess.
     */
    public String checkLibrary()
    {
   
    	return AccessController.doPrivileged(new PrivilegedAction<String>()
    	{
    		public String run()
    		{
				try
    			{
    				@SuppressWarnings("unused")
					Pkcs11Wrapper wrapper = new Pkcs11Wrapper();
    				return "1";
    			}
    			catch (Throwable e)
    			{
    				 StringWriter sw = new StringWriter();
    				 PrintWriter pw = new PrintWriter(sw);
    				 e.printStackTrace(pw);
    				 return sw.toString();
    			}
    		}
    	});
    }
    
    /**
     * Get token UID
	 * @return
     * 				Result token UID or null
     */
    public String getTerminalList()
    {
   
    	return AccessController.doPrivileged(new PrivilegedAction<String>()
    	{
    		public String run()
    		{
    			StringBuilder bld = new StringBuilder();
    			try
    			{
    				CardTerminals terminal = TerminalUtils.terminals();
    				for(CardTerminal t : terminal.list())
    				{
    					if (bld.length() > 0)
    					{
    						bld.append(",");
    					}
    					bld.append(t.getName());
    				}
    			}
    			catch (Throwable e)
    			{
    				 //e.printStackTrace();
    				 return "Error " + e.getMessage();
    			}
    			
    			return bld.toString();
    		}
    	});
    }
}
