
import javax.crypto.SealedObject;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.io.FileReader;
import java.io.BufferedReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.math.BigInteger;
import java.util.Calendar;
import java.util.StringTokenizer;
import java.util.ArrayList;
import java.text.SimpleDateFormat;

/**
 * Created by IntelliJ IDEA.
 * User: Johnson Gong
 * Date: 2006-3-11
 * Time: 3:43:48
 * To change this template use File | Settings | File Templates.
 */
public class Receive_RD {
    public static void main(String args[]) {
        try {
            String CAaddress = "localhost";
            int port;
            int CAport;

            FileReader fr = new FileReader("config");
            BufferedReader in = new BufferedReader(fr);
            in.readLine();
            port = Integer.parseInt(in.readLine());
            CAport = Integer.parseInt(in.readLine());
            in.close();
            fr.close();

            if (args.length < 1) {
                System.out.println("Usage: receive <username>");
                return;
            }

            if (args.length > 1) {
                port = Integer.parseInt(args[1]);
            }
            if (args.length > 2) {
                CAport = Integer.parseInt(args[2]);
            }

            String receiver = args[0];

            //The user's private key file is: username + ".priv"

            ServerSocket srvr = new ServerSocket(port);

            for (; ;) {
                Socket skt = srvr.accept();
                //System.out.print("[incoming message...]\n");

                ObjectInputStream objin = new ObjectInputStream(skt.getInputStream());

                byte[] encryptedAESKey = new byte[128];

                objin.read(encryptedAESKey);
                SealedObject sealedMsgSig = (SealedObject) objin.readObject();
                objin.close();
                skt.close();

                //TODO:
                //1. Read receiver's private key (e.g. "bob.priv")
                //2. Read sender's public key (e.g. "alice.pub")
                //3. Verify signature on revocation list
                //   before testing if sender is on list.
                //4. Decrypt and verify message.
                //   (This should be impossible if the message is intended
                //   for someone else.)
                //5. If message is banned then pass it on to the CA.
                //   Also include sender's signature as evidence
                //6. Otherwise print the timestamp, sender and message

                //Read Receiver's private key
                PrivateKey receiver_PrivateKey = Helper.readPrivateKey(receiver);


                //Decrypt AES Key
                Cipher cipherRSA = Cipher.getInstance(Helper.RSA_A);
                cipherRSA.init(Cipher.DECRYPT_MODE, receiver_PrivateKey);
                byte[] rawAESKey = cipherRSA.doFinal(encryptedAESKey);
                SecretKeySpec AESKeySpec = new SecretKeySpec(rawAESKey, "AES");


                //Decrypt Sealed Object from sender
                IvParameterSpec ivSpec = new IvParameterSpec(Helper.iv);
                Cipher cipherAES = Cipher.getInstance(Helper.AES_A);
                cipherAES.init(Cipher.DECRYPT_MODE, AESKeySpec, ivSpec);
                SignedObject_RD signedMessage = (SignedObject_RD) sealedMsgSig.getObject(cipherAES);
                Message msgReceived = signedMessage.getMessage();

                //Read Sender's Public Key
                String sender = msgReceived.getSender();
                //PublicKey sender_PublicKey = Helper.readPublicKey(sender);
                BigInteger[] sender_publicRDKey = Helper.readPublicKey_RD(sender);

                //Verify Signatrure
                //Signature sig = Signature.getInstance("SHA1withRSA");
                if (!signedMessage.verify(sender_publicRDKey[0], sender_publicRDKey[1])) {
                    System.out.println("Signature failed");
                    System.exit(0);
                }


                Calendar timestamp = msgReceived.getTimestamp();


                boolean is_revoked = Helper.isUserRevoked(sender);

                if (is_revoked) {
                    System.out.print("[Sender is revoked, ignoring message]\n");
                } else {
                    int i = Helper.is_banned_RD(msgReceived.getChat());
                    if (i >= 0){

                    System.out.print("[Banned message received. Notifying authorities...]\n");

                    Socket CAskt = new Socket(CAaddress, CAport);
                    ObjectOutputStream objout = new ObjectOutputStream(CAskt.getOutputStream());

                    BigInteger squealSig = signedMessage.getSquealSig(i);
                    String badWord = getBadWord(i,msgReceived.getChat());

                    Squeal_RD srd = new Squeal_RD(badWord,sender,squealSig);


                    objout.writeObject(srd);
                        System.out.println("Finish writing object");
                    objout.flush();
                    objout.close();
                    CAskt.close();

                } else {
                    SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss ");
                    System.out.print("(" + formatter.format(timestamp.getTime()) + ") ");
                    System.out.print(sender);
                    System.out.print(": ");
                    System.out.println(msgReceived.getChat());

                 } }}

            //Note: ideally there should be a more graceful way to stop the server
            //and then we'd call srvr.close();

        } catch (BadCertificateException e) {
            System.out.println(e);
            System.out.print("Sender's UserKeyPair is bad\n");
            System.exit(0);
        } catch (Exception e) {
            System.out.println(e);
            e.printStackTrace();
            System.out.print("Error\n");
        }

    }
    private static String getBadWord(int i, String message){
        StringTokenizer st = new StringTokenizer(message);
        ArrayList words = new ArrayList();
        while (st.hasMoreTokens()) {
            words.add(st.nextToken());
        }
        return (String)words.get(i);
        //return "";
    }
}
