/*
 * 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 _PKCS7_HH
#define _PKCS7_HH

#include <string>
#include <vector>

#include <openssl/opensslv.h>
#include <openssl/crypto.h>

#include "pkcs8.hh"
#include "Certificate.hh"


#include "env.hh"

#include "config.h"
#if defined(HAVE_EXT_HASH_MAP)
#include <ext/hash_map>
using namespace __gnu_cxx;
#else
#if defined(HAVE_HASH_MAP)
#include <hash_map>
#else
#error Cannot find hash_map header !
#endif
#endif


#if defined(HAVE_EXT_HASH_SET)
#include <ext/hash_set>
using namespace __gnu_cxx;
#else
#if defined(HAVE_HASH_SET)
#include <hash_set>
#else
#error Cannot find hash_set header !
#endif
#endif



namespace Cryptonit {
 
    //  class pkcs7Attribute;
  
    /**
       this class manages the pkcs7 file format and the cryptographic operations on these files.
       - Encrypting<br>
       - Decrypting<br>
       - Signing<br>
       - Verifying<br>
    */
    class pkcs7 {
    
    public:
	pkcs7();
	~pkcs7();
    
    
	/** set the content type of the pkcs7
	 * @param cType : contentType value, can be  PKCS7Data, PKCS7Signed, PKCS7Encrypted, PKCS7SignedEncrypted or NID_undef
	 */
	int setContentType(PKCS7ContentType cType);
    
	/** get the content type of the current pkcs7
	 *
	 */
	PKCS7ContentType getContentType();


	/** set the Cipher type
	 * @param name : cypher type name, can be C_DES, C_DES3, C_RC2_128, C_RC2_64 or C_RC2_40
	 */    
	int setCipher(CipherName name);
	
	int setCipher(std::string name);
    
	/** set the Digest type
	 *  @param name : digest type name, can be D_MD2,_MD5 or D_SHA1
	 */
	void setDigest(DigestName name);
	void setDigest(std::string name);
	
    
	/** add a recipient of this pkcs7
	 * @param recipient : a recipient's Certificate
	 */
	int addRecipient(Certificate &recipient);

    
	/** add the recipients of this pkcs7
	 * @param recipients : a vector of recipients
	 */
	int addRecipients(std::vector <Certificate> &recipients);

    
	/** encrypt a file
	 * @param filein : filename of the file to encrypt
	 */
	int encrypt(const char *filein);


	/** save a pkcs7 file
	 * @param fileout : filename where to save the pkcs7
	 * @param format : output file format (pem_format or der_format)
	 */
	bool write(const char *fileout , FileFormat format=der_format);
    
	/** read a pkcs7 file
	 * @param filein : a pkcs7 file
	 * @param format : format of the pkcs7 file
	 */
	int read(const char *filein, FileFormat format);

	int read(const char *filein);
      

	/** save a decrypted file
	 * @param fileout : a filename
	 */
	int writeDecrypted(const char *fileout);
    

	/** decrypt a pkcs7 file 
	 * @param privateKey : a private Key used for decrypting
	 * @param cert : a Certificate
	 */
	int decrypt(Key *privateKey, Certificate *cert);
    
      
	/** sign a file
	 * @param filein : the file to sign
	 * @param cert : the signer's certificate
	 * @param privateKey : the signer's private key
	 * @param certChain : a chain of certificates
	 * @param detached : wether the signature is detached or not (default FALSE)
	 */
	int sign(const char *filein, 
		 Certificate &cert, 
		 Key &privateKey, 
		 std::vector<Certificate> &certChain,
		 bool detached = false);

 
	/** sign and encrypt a file
	 * @param filein : the file to sign and encrypt
	 * @param cert : the signer's certificate
	 * @param privateKey : the signer's private key
	 * @param certChain : a chain of certificates
	 * @param recipient : the recipent's certificate
	 */
	int signAndEncrypt(const char *filein, Certificate &cert, 
			   Key &privateKey, 
			   std::vector<Certificate> &certChain, 
			   std::vector<Certificate> &recipients);


	/** extract a signed file
	 */
	bool extract(pkcs8 &k , Certificate &recipient);
    
    
	/** write an extracted file
	 * @param fileout : a file where the extracted data will be written.
	 */
	bool writeExtracted(const char *fileout);


	/** Check if the pkcs7 is signed
	 * @return true if the pkcs7 is signed, false else.
	 */
	bool isSigned();
    
	/** Check if the pkcs7 is encrypted
	 * @return true if the pkcs7 si encryped, false else.
	 */
	bool isEncrypted();
    
	/** Check if the pkcs7 is signed and encrypted
	 * @return true if the pkcs7 is signed and encrypted, false else.
	 */
	bool isSignedAndEncrypted();

	bool isDetached();


	/** get the certificates from the pkcs7
	 * @return a vector of Certificate
	 */
	std::vector<Certificate> &getCertificates();

	/** get the recipients certificates from the pkcs7
	 * @return a vector of Certificate
	 */
	std::vector<Certificate> &getRecipientsCertificates();

    
	std::vector<Crl> &getCRL();
	

	X509_STORE *makeStore(const char *CAPath);
	
	int verify(Certificate &signer,pkcs8 &recipientPrivateKey, Certificate &recipientCert, const char *CAdir ,const char* data=NULL );

	
	bool verifyCert(hash_map<Certificate, std::vector<Crl>, Certificate::Certificate_hash_str, Certificate::eqstr> CACRL, std::vector<Certificate> *CA, void (*callback)(std::string filename, std::string m, void *statusDialog, bool ok), std::string filename, void *statusDialog);
	
	std::vector<Certificate> *getChainFromSigned(std::vector<Certificate> *CAList);
	
	/* same as OpenSSL PKCS7_sign(),but take care of what kind of digest 
	 * has been set.
	 */
	PKCS7 *my_PKCS7_sign(X509 *signcert, EVP_PKEY *pkey, STACK_OF(X509) *certs,
			     BIO *data, int flags);

	std::vector<PKCS7_RECIP_INFO>  &getRecipientInfos();

	Certificate& getSignerCertificate();
	    
    private:
	PKCS7 *p7; 
	PKCS7ContentType contentType;
	BIO *p7bio; 
	std::vector<Certificate> certs;
    

#if (OPENSSL_VERSION_NUMBER>=0x00907000L)
	const EVP_CIPHER *cipher ;
	const  EVP_MD *digest;
#else
	EVP_CIPHER *cipher ;
	EVP_MD *digest;
#endif

    };

}


#endif
