/*
 * The Cryptonit security software suite is developped by IDEALX
 * Cryptonit Team (http://IDEALX.org/ and http://cryptonit.org).
 *
 * Copyright 2003-2006 IDEALX
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 as published by the Free Software Foundation.
 * 
 * This program 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301, USA. 
 *
 * In addition, as two special exceptions:
 *
 * 1) IDEALX S.A.S gives permission to:
 *  * link the code of portions of his program with the OpenSSL library under
 *    certain conditions described in each source file
 *  * distribute linked combinations including the two, with respect to the
 *    OpenSSL license and with the GPL
 *
 * You must obey the GNU General Public License in all respects for all of the
 * code used other than OpenSSL. If you modify file(s) with this exception,
 * you may extend this exception to your version of the file(s), but you are
 * not obligated to do so. If you do not wish to do so, delete this exception
 * statement from your version, in all files (this very one along with all
 * source files).

 * 2) IDEALX S.A.S acknowledges that portions of his sourcecode uses (by the
 * way of headers inclusion) some work published by 'RSA Security Inc.'. Those
 * portions are "derived from the RSA Security Inc. PKCS #11Cryptographic
 * Token Interface (Cryptoki)" as described in each individual source file.
 */


#ifndef _Certificate_hh
#define _Certificate_hh
 
#include <vector>
#include <string>

#include <openssl/x509.h>
#include <openssl/pkcs12.h>
#include <openssl/pem.h>

#include "env.hh"
#include "Dn.hh"
#include "Crl.hh"


#define VERIF_CERTIFICATE_IS_VALID    0x0001 //1
#define VERIF_CERTIFICATE_IS_REVOKED  0x0002 //2
#define VERIF_CRL_IS_OUT_DATED        0x0004 //4
#define VERIF_CRL_NO_SIGNER_FOUND     0x0008 //8
#define VERIF_CHAIN_NOT_VALID         0x0010 //16
#define VERIF_ROOT_NOT_SELF_SIGNED    0x0020 //32
#define VERIF_CRL_NOT_FOUND           0x0040 //64
#define VERIF_CERTIFICATE_EXPIRED     0x0080 //128


namespace Cryptonit {


  class Crl;


/**
 * Certificate
 * Class ables to load a certifiate from several types.
 *
 */
    class Certificate
    {
	
    private:
	/* the certificate */
	X509* certificate;
	
	// return the issuer of the certificate if it is in the list
	int getIssuerFromList(std::vector<Certificate> certList, Certificate& issuer);
	
	/** Give the chain of certification 
	 * 
	 * 
	 * @param pathToRoot list of all certificates
	 * @param certList all the certs present in a certain directory
	 * @param error 
	 * 
	 * @return a vector of certificate representing the chain of certification
	 */
	std::vector<Certificate> *getPathToRootRec(std::vector<Certificate> *pathToRoot
						   , std::vector<Certificate> *certList
						   , bool *error);


     public:



	// Hash function for Certificate class
	struct Certificate_hash_str
	{
	    inline size_t __stl_hash_string(const char* __s) const
	    {
		unsigned long __h = 0; 
		for ( ; *__s; ++__s)
		    __h = 5*__h + *__s;
		
		return size_t(__h);
	    }
	    
	    size_t operator()(Certificate c) const
	    {
		return  __stl_hash_string( c.getHash().c_str() );
	    }
	};
	
	struct eqstr {
	    bool operator()(Certificate c1, Certificate c2) const {
		return  (strcmp(c1.getHash().c_str() , c2.getHash().c_str())==0);
		//X509_cmp(c1.getX509Certificate() , c2.getX509Certificate());
	    }
	};





    /** Create the object certificate 
     * 
     */
    Certificate();
	
    /** Create the object certificate from a file 
     * @param _fileName : the file name of the certificate to be load
     * @param format : format of the cerificate file (pem_format or der_format)
     */
    Certificate(const char* _fileName, FileFormat format);
	

    /** Create the object certificate from a buffer of size 'len'.
     *	If 'len' is -1 then the buffer is assumed to be null terminated 
     *  and its length is determined by strlen().
     *  The certificate format have to be DER format.
     *
     * @param buffer : a buffer containing a DER encoded certificate
     * @param length : the length of the buffer, -1 if it is NULL terminated.
     */
    Certificate( void* buffer, int length );


    /**copy constructor
     * @param a certificate
     */
    Certificate(const Certificate &c);
    

    /** Destruct the object */
    ~Certificate();


    /**
     * To free the X509 certificate 
     */
    void free();


    /** load a certificate file
     *  @_fileName : the certificate file to load
     */
    int load(const char* _fileName);

    /** load a certificate file
     * @param _fileName : the certificate file to load
     * @param format : the format of the certificate file (pem_format or der_format)
     */
    int load(const char* _fileName, FileFormat format);
	


    /** load a certificate from LDAP
     *
     */
    int load(std::string hostname, std::string port , std::string base, std::string id);


       int load( void* buffer, int len );

       

    /** save a certificate in a file
     * @param filename : the target filename
     * @param format : a format for the saved certificate (pem or der)
     */
    int save(const char* filename, FileFormat format=der_format);
    

    /** Verify if this certificate can be used.
     * ex: verification of the date of validity.
     *
     * @param _revokedList : the revocation list.
     * @return XSC_ERROR : the error number.
     */
    //  int verify(XSC_RevokedList _revokedList);
	

    /** Test if the certificate is revoked.
     * 
     * @return true if the certificate is not revoked, false else.
     */	
    int isValid(std::vector<Crl> *crl, std::vector<Certificate> *CA=NULL);
	
    /** Send the list of parents' certificates.
     * 
     * @return the list of parents' certificates.
     */
    Certificate *getCaList();

    /**
     * get the X509 Certificate 
     */
    X509* getX509Certificate();


    /** set the X509 Certificate
     * @param 
     */
    bool setX509Certificate(X509 *certif);
  

    /**
     * return the version's number
     * @return the version's number
     */
    std::string getSerialNumber() const;
  
    int getVersion() const;
    std::string getVersionString() const;

  
    /** get the certificate's subject name
     * @return a dn with the SubjectName
     */
    dn getSubjectName() const;
  
    /** get the certificate issuer name
     * @return a dn with the IssuerName
     */
    dn getIssuerName() const;


    /** get the expiration date of the certificate
     * @return the end date.
     */
    unsigned char* getEndDate();

    /** get the validity starting date of the certificate
     * @return the start date.
     */
    unsigned char* getStartDate();

    /** Check the time validity of the certificate
     * @return true if the certificate's date is valid, and fasle otherwise (not expired or not anticipated)   
     */
    bool timeValidity() const;
  
    /** print the X509 V3 extensions
     * @return a string containing the extensions.
     */
    std::string printV3ext();

  
    /** print the X509 V3 extensions
     * @param cert : x509 certificate from which getting the extensions
     *
     * @return a string containing the extensions.
     */
    std::string printV3ext(X509 *cert);


    /** display in a nicer way the start date
     * @return a string
     */
    std::string getPrettyStartDate();

    /** display in a nicer way the end date
     * @return a string
     */
    std::string getPrettyEndDate();
  
    /** get the X509v3 Extension of the certificate
     * @return a vector of string
     */
    std::vector<std::string> getV3ext();
  
  
    /** get the validation chain for the certificate
     * @param certList a vector of the available certificate
     * @return a vector of the certificates of the validation chain.
     */
    std::vector<Certificate> *getPathToRoot(std::vector<Certificate> *certList);
  
       std::vector<Certificate> *getChain(std::vector<Certificate> *certList);

    /** check if the certificate is for signing 
     * @return a boolean
     */
    bool isForSigning();
  
    /** check if the certificate is for encrypting
     * @return a boolean
     */
    bool isForEncrypting();
    
    /** check if the certificate is for signing/encrypting
     * @return a boolean
     */
    bool isForSigningAndEncrypting();


    /** check if the certificate is a CA certificate
     * @return a boolean
     */
    bool isCA();

    /** check if the certificate usage is signing CRL
     * @return a boolean
     */
    bool isForSigningCRL();

    /** Check if a Certificate is revoked (ie it is present in the CRL)
     *
     * @return true if certificate is revoked, false else.
     */
    bool isRevoked(Crl *crl);

  
    int check_is_issuer(X509 *a, X509 *ca);
    static int ca_check(const X509 *x);

    int isIssued(const Certificate &issuer);


//    Certificate& operator= (X509 *x);

    /** extract the uri where to get the CRL
     * @return a string containing the uri
     */
    std::string extractCrlUri();
  

    /** return the hash of the certificate
     * @return a string containing the hash
     */
    std::string getHash();

    /* return the subject email if presents un X509v3 extension
     * @return a string containing the email address.
     */
    std::string getEmailAddress();


    X509_STORE* createStore ();


    Certificate& operator=( const Certificate &src);

  };
}
#endif
