package com.limegroup.gnutella.updates;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.UnsupportedEncodingException;
import java.security.PublicKey;

import com.bitzi.util.Base32;
import com.limegroup.gnutella.ErrorService;
import com.limegroup.gnutella.security.SignatureVerifier;
import com.limegroup.gnutella.util.CommonUtils;

import org.apache.commons.logging.LogFactory;
import org.apache.commons.logging.Log;

/**
 * Provides static methods, which accept an InputStream and use the 
 * LimeWire public key to verify that the contents are authentic.
 */
public class UpdateMessageVerifier {
    
    private static final Log LOG = LogFactory.getLog(UpdateMessageVerifier.class);

    private byte[] data;
    private byte[] signature;
    private byte[] xmlMessage;
    private boolean fromDisk;
    
    /**
     * @param fromDisk true if the byte are being read from disk, false is the
     * bytes are being read from the network
     */
    public UpdateMessageVerifier(byte[] fromStream, boolean fromDisk) {
        if(fromStream == null)
            throw new IllegalArgumentException();
        this.data = fromStream;
        this.fromDisk = fromDisk;
    }
    
    
    public boolean verifySource() {        
        //read the input stream and parse it into signature and xmlMessage
        boolean parsed = parse(); 
        if(!parsed)
            return false;

        //get the public key
        PublicKey pubKey = null;
        FileInputStream fis = null;
        ObjectInputStream ois = null;
        try {
            File file = 
                new File(CommonUtils.getUserSettingsDir(),"public.key");
            fis = new FileInputStream(file);
            ois = new ObjectInputStream(fis);
            pubKey = (PublicKey)ois.readObject();
        } catch(Throwable t) {
            LOG.error("Unable to read public key", t);
            return false;
        } finally {
            if(ois != null) {
                try {
                    ois.close();
                } catch (IOException e) {
                    // we can only try to close it...
                }
            } 
            if(fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    // we can only try to close it...
                }
            }       
        }
        
        SignatureVerifier verifier = 
                    new SignatureVerifier(xmlMessage,signature, pubKey, "DSA");
        
        return verifier.verifySignature();
    }

    private boolean parse() {
        int i;
        int j;
        i = findPipe(0);
        j = findPipe(i+1);
        if(i<0 || j<0) //no 2 pipes? this file cannot be the real thing, 
            return false;
        if( (data.length - j) < 10) //xml smaller than 10? no way
            return false;
        //now i is at the first | delimiter and j is at the second | delimiter
        byte[] temp = new byte[i];
        System.arraycopy(data,0,temp,0,i);
        String base32 = null;
        try {
            base32 = new String(temp, "UTF-8");
        } catch(UnsupportedEncodingException usx) {
            ErrorService.error(usx);
        }
        signature = Base32.decode(base32);
        xmlMessage = new byte[data.length-1-j];
        System.arraycopy(data,j+1,xmlMessage,0,data.length-1-j);
        return true;
    }
    
    /**
     * @return the index of "|" starting from startIndex, -1 if none found in
     * this.data
     */
    private int findPipe(int startIndex) {
        byte b = (byte)-1;
        boolean found = false;
        int i = startIndex;
        for( ; i < data.length; i++) {
            if(data[i] == (byte)124) {
                found = true;
                break;
            }
        }
        if(found)
            return i;
        return -1;
    }

    public byte[] getMessageBytes() throws IllegalStateException {
        if(xmlMessage==null)
            throw new IllegalStateException();
        return xmlMessage;
    }
}
