/*
 * 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.
 */

#include "pkcs7.hh"


namespace Cryptonit {

    pkcs7::pkcs7()
    {
	//certs = sk_X509_new_null();
	p7 = PKCS7_new();
	p7bio=NULL;
	cipher = NULL;
	digest = NULL;
    }

    pkcs7::~pkcs7()
    {
	PKCS7_free(p7);
	certs.clear();
    }

    int pkcs7::setContentType(PKCS7ContentType cType)
    {
	if(p7) {
	    if(cType == PKCS7Data || cType ==  PKCS7Signed ||
	       cType == PKCS7Encrypted || cType == PKCS7SignedEncrypted) {
		return(PKCS7_set_type(p7,cType));
	    } else {
		return(PKCS7_set_type(p7,NID_undef));
	    }
	}
	return 0;
    }


    PKCS7ContentType pkcs7::getContentType()
    {
	return (PKCS7ContentType)OBJ_obj2nid(p7->type);
    }


    int pkcs7::setCipher(CipherName name)
    {
	if(p7) {
	    switch(name) {
#ifndef OPENSSL_NO_DES
	    case C_DES:
		cipher = EVP_des_cbc();
		break;
	
	    case C_DES3:
		cipher = EVP_des_ede3_cbc();
		break;
#endif
#ifndef OPENSSL_NO_RC2     
	    case C_RC2_128:
		cipher = EVP_rc2_cbc();
		break;
      
	    case C_RC2_64:
		cipher = EVP_rc2_64_cbc();
		break;
      
	    case C_RC2_40:
		cipher = EVP_rc2_40_cbc();
		break;
#endif
#ifndef OPENSSL_NO_AES      
	    case C_AES_128:
		cipher = EVP_aes_128_cbc();
		break;

	    case C_AES_192:
		cipher = EVP_aes_192_cbc();
		break;

	    case C_AES_256:
		cipher = EVP_aes_256_cbc();
		break;
#endif	

	    default:
		return 0;
		break;
	    }
	    return PKCS7_set_cipher(p7,cipher);
	}
	return 0;
    }




    int pkcs7::setCipher(std::string name)
    {
	bool ok = false;
	if(p7) {
	    
#ifndef OPENSSL_NO_DES
	    if(name == "DES"){
		cipher = EVP_des_cbc();
		ok = true;
	    }
	    
	    if (name == "DES3"){
		cipher = EVP_des_ede3_cbc();
		ok = true;
	    }

	    if (name == "DESX"){
		cipher = EVP_desx_cbc();
		ok = true;
	    }
#endif
#ifndef OPENSSL_NO_RC2     
	    if (name == "RC2-128"){
	      cipher = EVP_rc2_cbc();
		ok = true;
	    }

      	    if (name == "RC2-64"){
		cipher = EVP_rc2_64_cbc();
		ok = true;
	    }
	    
	    if (name == "RC2-40"){
		cipher = EVP_rc2_40_cbc();
		ok = true;
	    }
#endif
#ifndef OPENSSL_NO_RC4
	    if(name == "RC4"){
		cipher = EVP_rc4();
		ok = true;
	    }
#endif
#ifndef OPENSSL_NO_RC5
	    if(name == "RC5"){
		cipher = EVP_rc5_32_12_16_cbc();
		ok = true;
	    }   
#endif
#ifndef OPENSSL_NO_IDEA
	    if(name == "IDEA"){
		cipher = EVP_idea_cbc();
		ok = true;
	    }   	  
#endif
#ifndef OPENSSL_NO_BF
	    if(name == "BLOWFISH"){
		cipher = EVP_bf_cbc();
		ok = true;
	    }
#endif
#ifndef OPENSSL_NO_CAST
	    if( name == "CAST"){
		cipher = EVP_cast5_cbc();
		ok = true;
	    }
	    
#endif
#ifndef OPENSSL_NO_AES      
	  if (name == "AES-128"){
	      cipher = EVP_aes_128_cbc();
	      ok = true;
	  }
	  
	  if(name == "AES-192"){
	      cipher = EVP_aes_192_cbc();
	      ok = true;
	  }
	  
	  if( name =="AES-256"){
	      cipher = EVP_aes_256_cbc();
	      ok = true;
	  }
#endif	
	  if(ok)
	      return PKCS7_set_cipher(p7,cipher);
	}
	return 0;
    }


  

    void pkcs7::setDigest(DigestName name)
    {
	switch(name) {
	case D_MD2:
	    digest = EVP_md2();
	    break;
	case D_MD5:
	    digest = EVP_md5();
	    break;
	case D_SHA1:
	    digest = EVP_sha1();
	    break;
	default:
	    break;
	}
    }



    void pkcs7::setDigest(std::string name)
    {
#ifndef OPENSSL_NO_MD2
	if (name == "MD2") {
	    digest = EVP_md2();
	    return;
	}
#endif
#ifndef OPENSSL_NO_MD4 	
	if (name == "MD4") {
	    digest = EVP_md4();
	    return;
	}
#endif
#ifndef OPENSSL_NO_MD5	
	if (name =="MD5") {
	    digest = EVP_md5();
	    return;
	}
#endif
#ifndef OPENSSL_NO_SHA
	if (name == "SHA1") {
	    digest =  EVP_sha1();
	    return;
	}
#endif	
#if !defined(OPENSSL_NO_MDC2) && !defined(OPENSSL_NO_DES)
	if (name == "MDC2") {
	    digest = EVP_mdc2();
	    return;
	}
#endif
#ifndef OPENSSL_NO_RIPEMD	
	if (name == "RIPEMD" ) {
	    digest = EVP_ripemd160();
	    return;
	}
#endif
	
    }



  
    bool pkcs7::write(const char *fileout, FileFormat format)
    {
	if( getContentType() == PKCS7Signed 
	    || getContentType() == PKCS7Encrypted 
	    || getContentType() == PKCS7SignedEncrypted ) {
      
	    BIO *out;
	    if( !(out = BIO_new_file(fileout, "wb")) ) {
#ifdef DEBUG
 		std::cout << getErrorMsg(PKCS7_BIO_NEW_ERROR) << std::endl;
#endif 
		return PKCS7_BIO_NEW_ERROR;
	    }
	    if( format == pem_format ) {
		if( PEM_write_bio_PKCS7(out,p7) ) {
		    BIO_free_all(out);
		    return SUCCESS;	
		} else {
		    BIO_free_all(out);
#ifdef DEBUG
 		    std::cout << getErrorMsg(PKCS7_BIO_WRITE_ERROR) << std::endl;
#endif 
		    return PKCS7_BIO_WRITE_ERROR;
		}
	    }
	    else if( format == der_format ) {
		if( i2d_PKCS7_bio(out,p7) ) {
		    BIO_free_all(out);
		    return SUCCESS;
		} else {
		    BIO_free_all(out);
#ifdef DEBUG
 		    std::cout << getErrorMsg(PKCS7_BIO_WRITE_ERROR) << std::endl;
#endif 
		    return PKCS7_BIO_WRITE_ERROR;
		}
	    }
#ifdef DEBUG
 	    std::cout << getErrorMsg(PKCS7_BAD_FORMAT) << std::endl;
#endif 
	    return PKCS7_BAD_FORMAT;
	}
#ifdef DEBUG
 	std::cout << getErrorMsg(PKCS7_BAD_CONTENT_TYPE) << std::endl;
#endif 
	return PKCS7_BAD_CONTENT_TYPE;
    }

    
    int pkcs7::writeDecrypted(const char *fileout)
    {
	FILE *fout;
	unsigned char buf[65535];
	
	fout = fopen( fileout , "wb" );
	
	if(fout) {
	    int i = 0;
	    while( true ) {
		i = BIO_read(p7bio, buf, sizeof(buf));
		if (i <= 0) break;
		fwrite(buf,  sizeof(char), i, fout);
	    }
	    fclose(fout);
	    BIO_free(p7bio);
	    p7bio = NULL;
	    return SUCCESS;
	}
	return  PKCS7_FILE_CREATE_ERROR;
    }
  

    int pkcs7::read(const char *filein, FileFormat format)
    {
	FILE *fin;
	fin=fopen(filein, "r" );
    
	if(fin) {
	    if(p7) {
		PKCS7_free(p7);
		p7 = NULL;
	    }
      
	    if( format==pem_format )
		p7 = PEM_read_PKCS7(fin, NULL, NULL, NULL);
	    else if (format==der_format)
		p7=d2i_PKCS7_fp(fin,NULL);
      
	    fclose(fin);
	    if(p7)
		return SUCCESS;
      
	    return PKCS7_READ_ERROR;
	}
	else
	    return PKCS7_FILE_NOT_FOUND;
    }
  


    int pkcs7::read(const char *filein)
    {
	if(read(filein, pem_format) != SUCCESS)
	    if(read(filein, der_format) != SUCCESS)
		return PKCS7_READ_ERROR;
	return SUCCESS;
    }

  
    int pkcs7::addRecipient(Certificate &recipient)
    {
	if(p7) {
	    //      PKCS7_add_recipient(p7, recipient.getX509Certificate());
	    //sk_X509_push(certs, recipient.getX509Certificate());
	    certs.push_back(recipient);
	    return 1;
	}
	return 0;
    }
  
  
    int pkcs7::addRecipients(std::vector<Certificate> &recipients)
    {
	int i = 0;
	if(p7) {
	    i=1;
	    for( std::vector<Certificate>::iterator it = recipients.begin() ; it != recipients.end() ; it++ )
		i += addRecipient(*it);
	}
	return i;
    }
  

    int pkcs7::encrypt(const char *filein)
    {
	BIO *in;

	if (!(in = BIO_new_file(filein, "r")))
	    return  PKCS7_BIO_NEW_ERROR;

	std::vector<Certificate>::iterator it;
	
	STACK_OF(X509) *stack = sk_X509_new_null();
    
	for(it = certs.begin(); it != certs.end() ; it++) {
	    sk_X509_push(stack,  it->getX509Certificate());
	}
    
	if((p7 = PKCS7_encrypt( stack, in ,cipher , PKCS7_BINARY  )) == NULL)
	    return PKCS7_ENCRYPT_ERROR;
    
	sk_X509_pop_free(stack, X509_free);
	
	BIO_free(in);
	
	return SUCCESS;    
    }

  
    int pkcs7::decrypt(Key *pkey, Certificate *cert)
    {
	return ((p7bio = PKCS7_dataDecode(p7, pkey->getPrivateKey(), NULL, 
					  cert->getX509Certificate())) == NULL) ?
	    PKCS7_DECRYPT_ERROR : SUCCESS;
    }

    int pkcs7::sign(const char *filein, Certificate &cert, Key &privateKey,
		    std::vector<Certificate> &certChain, bool detached)
    {
	STACK_OF(509) *chain=NULL;
	std::vector<Certificate>::iterator certIt;
	X509 *certif = cert.getX509Certificate();
	EVP_PKEY *pkey = privateKey.getPrivateKey();
	BIO *in;
	int flag = PKCS7_BINARY;
	p7bio = NULL;
    

	if(!X509_check_private_key(certif, pkey)) {
	    PKCS7err(PKCS7_F_PKCS7_SIGN,PKCS7_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE);
	    return PKCS7_PKEY_CERT_MISMATCH;
	}

	if (!(in = BIO_new_file(filein, "r"))) {
	  BIO_free(in);
	  return PKCS7_BIO_NEW_ERROR;
	}
	
	if( (certif != NULL) && (pkey !=NULL) ){
	    chain = sk_X509_new_null();
	    for(certIt=certChain.begin(); certIt!=certChain.end(); certIt++)
		sk_X509_push(chain, certIt->getX509Certificate());
	    
	    if(detached)
		flag = PKCS7_BINARY | PKCS7_DETACHED;
	    
	    if(!(p7 = my_PKCS7_sign(certif,pkey, chain, in, flag ))){
	      BIO_free(in);
		 return PKCS7_SIGN_ERROR;
	    }
	    BIO_free(in);
	    return SUCCESS;
	}
	BIO_free(in);
	return PKCS7_CERT_OR_KEY_NULL;
    }
  
  

    PKCS7 *pkcs7::my_PKCS7_sign(X509 *signcert, EVP_PKEY *pkey, STACK_OF(X509) *certs,
				BIO *data, int flags)
    {
	PKCS7 *p7;
	PKCS7_SIGNER_INFO *si;
	BIO *p7bio;
	STACK_OF(X509_ALGOR) *smcap;
	int i;
	
	if(!X509_check_private_key(signcert, pkey)) {
	    PKCS7err(PKCS7_F_PKCS7_SIGN,PKCS7_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE);
	    return NULL;
	}
	
	if(!(p7 = PKCS7_new())) {
	    PKCS7err(PKCS7_F_PKCS7_SIGN,ERR_R_MALLOC_FAILURE);
	    return NULL;
	}
	
	PKCS7_set_type(p7, NID_pkcs7_signed);
	
	PKCS7_content_new(p7, NID_pkcs7_data);
	
	if(digest == NULL)
	    setDigest("SHA1");
	
    	if (!(si = PKCS7_add_signature(p7,signcert,pkey,digest))) {
		PKCS7err(PKCS7_F_PKCS7_SIGN,PKCS7_R_PKCS7_ADD_SIGNATURE_ERROR);
		return NULL;
	}
	
	if(!(flags & PKCS7_NOCERTS)) {
	    PKCS7_add_certificate(p7, signcert);
	    if(certs) for(i = 0; i < sk_X509_num(certs); i++)
		PKCS7_add_certificate(p7, sk_X509_value(certs, i));
	}
	
	if(!(p7bio = PKCS7_dataInit(p7, NULL))) {
	    PKCS7err(PKCS7_F_PKCS7_SIGN,ERR_R_MALLOC_FAILURE);
	    return NULL;
	}
	
	
	SMIME_crlf_copy(data, p7bio, flags);
	
	if(!(flags & PKCS7_NOATTR)) {
	    PKCS7_add_signed_attribute(si, NID_pkcs9_contentType,
				       V_ASN1_OBJECT, OBJ_nid2obj(NID_pkcs7_data));
	    /* Add SMIMECapabilities */
	    if(!(flags & PKCS7_NOSMIMECAP))
		{
		    if(!(smcap = sk_X509_ALGOR_new_null())) {
			PKCS7err(PKCS7_F_PKCS7_SIGN,ERR_R_MALLOC_FAILURE);
			return NULL;
		}
#ifndef OPENSSL_NO_DES
		    PKCS7_simple_smimecap (smcap, NID_des_ede3_cbc, -1);
#endif
#ifndef OPENSSL_NO_RC2
		    PKCS7_simple_smimecap (smcap, NID_rc2_cbc, 128);
		    PKCS7_simple_smimecap (smcap, NID_rc2_cbc, 64);
#endif
#ifndef OPENSSL_NO_DES
		    PKCS7_simple_smimecap (smcap, NID_des_cbc, -1);
#endif
#ifndef OPENSSL_NO_RC2
		    PKCS7_simple_smimecap (smcap, NID_rc2_cbc, 40);
#endif
		    PKCS7_add_attrib_smimecap (si, smcap);
		    sk_X509_ALGOR_pop_free(smcap, X509_ALGOR_free);
		}
	}
	
	if(flags & PKCS7_DETACHED)PKCS7_set_detached(p7, 1);
	
        if (!PKCS7_dataFinal(p7,p7bio)) {
	    PKCS7err(PKCS7_F_PKCS7_SIGN,PKCS7_R_PKCS7_DATASIGN);
	    return NULL;
	}
	
        BIO_free_all(p7bio);
	return p7;
    }
    
    
    
    
  
    int pkcs7::signAndEncrypt(const char *filein, Certificate &cert, 
			      Key &privateKey, std::vector<Certificate> &certChain,  
			      std::vector<Certificate> &recipients)
    {
	FILE *fin; 
	unsigned char buf[65535];
	X509 *recipientCert = NULL;
	X509 *signerCert = cert.getX509Certificate();
	EVP_PKEY *pkey = privateKey.getPrivateKey();
	PKCS7_SIGNER_INFO *signerInfo=NULL;
    
	if((fin=fopen(filein,"rb"))!=NULL) {
	    
	    if(!digest)
		setDigest( D_SHA1 );
	    
	    if(p7) {
		PKCS7_free(p7);
		p7=NULL;
	    }
	  
	    p7 = PKCS7_new();
	  
	    if(p7) {
		 
		PKCS7_set_type(p7,NID_pkcs7_signedAndEnveloped);
		PKCS7_content_new(p7,NID_pkcs7_data);
	      
		if(!PKCS7_set_cipher(p7,cipher))
		    return PKCS7_SET_CIPHER_ERROR;
		
		std::vector<Certificate>::iterator it = recipients.begin();
		while(it != recipients.end()){
		    recipientCert = it->getX509Certificate();
		    if(!PKCS7_add_recipient(p7,recipientCert))
			return  PKCS7_ADD_RECIPIENT_ERROR;
		    it++;
		}
 		if((signerInfo = PKCS7_add_signature(p7, signerCert, pkey, digest))==NULL)
 		    return PKCS7_ADD_SIGN_ERROR ;
		
		// 		PKCS7_add_signed_attribute(signerInfo, NID_pkcs9_contentType, V_ASN1_OBJECT, OBJ_nid2obj(NID_pkcs7_data));
		// 		PKCS7_add_certificate(p7,signerCert);
		// 		PKCS7_set_detached(p7, 0);
	
 		if ((p7bio=PKCS7_dataInit(p7,NULL)) == NULL)
 		    return PKCS7_DATAINIT_ERROR;
		
 		while(true) {
		    int j = fread(buf, sizeof(char), 65535, fin);
		    if (j <= 0) break;
		    BIO_write(p7bio, buf, j);
		}
 		BIO_flush(p7bio);
 		if(!PKCS7_dataFinal(p7,p7bio))
 		    return PKCS7_DATAFINAL_ERROR;
 		BIO_free(p7bio);
		return SUCCESS;
	    }
	  
	    return PKCS7_CREATE_ERROR;
	}
	return PKCS7_FILE_CREATE_ERROR;
    }
  


    bool pkcs7::extract(pkcs8 &k, Certificate &recipient)
    {
	X509* cert = NULL;
	EVP_PKEY *pkey = NULL;

	if(&recipient != NULL)
	    cert = recipient.getX509Certificate();
	if(&k != NULL)
	    pkey = k.getKey();
	bool ret = false;
	
	if(isSignedAndEncrypted()) {
	    if( (p7bio = PKCS7_dataDecode(p7, pkey,NULL, cert)) ==NULL)
		ret = false;
	    else
		ret = true;
	} else {
	    if((p7bio = PKCS7_dataInit(p7, NULL)) == NULL)
		ret = false;
	    else 
		ret = true;
	}
	
	X509_free(cert);
	EVP_PKEY_free(pkey);
	return ret;
    }
  
  
    bool pkcs7::writeExtracted(const char* fileout)
    {
	return writeDecrypted(fileout);
    }
    
    
    
    X509_STORE *pkcs7::makeStore(const char *CAPath)
    {
	X509_STORE *store;
 	X509_LOOKUP *lookup;
 	if(!(store = X509_STORE_new())) goto end;
 	lookup=X509_STORE_add_lookup(store,X509_LOOKUP_hash_dir());
	if (lookup == NULL) goto end;
	
	if (CAPath) {
	    if(!X509_LOOKUP_add_dir(lookup, CAPath, X509_FILETYPE_ASN1)) {
#ifdef DEBUG
 		std::cout << "--> X509_LOOKUP_add_dir" << std::endl;
#endif 
		goto end;
	    }
	} else {
	    X509_LOOKUP_add_dir(lookup,NULL,X509_FILETYPE_DEFAULT);
	}

	//adding CRL
	// 	if(X509_load_crl_file(lookup, _CRL_FILE_ , X509_FILETYPE_PEM ) != 1)
	// 	    {
 	// 		std::cout << "load CRL " << std::endl;
	// 		goto end;
	// 	    }
	
	ERR_clear_error();
	return store;

    end:
#ifdef DEBUG
 	std::cout << "ERREUR" << std::endl;
#endif 
	X509_STORE_free(store);
	return NULL;
    }
   
	
  
    /** /!\ just check signature not Certificate against CA**/
    
    int pkcs7::verify(Certificate &signer,pkcs8 &recipientPrivateKey, 
		      Certificate &recipientCert, const char* CAdir, const char* data )
    {
	X509_STORE *store = NULL;

	Certificate extractedSigner = getCertificates().back();

	X509 *signerCertif = signer.getX509Certificate();
	X509 *extractedSignerCertif = extractedSigner.getX509Certificate();
	X509 *recipientCertif = recipientCert.getX509Certificate();
	EVP_PKEY *pkey = NULL;
	
	if(&recipientPrivateKey != NULL)
	    pkey = recipientPrivateKey.getKey();
	int ret = SUCCESS;
	
	p7bio=NULL;
	
	if((!isSigned()) && (!isSignedAndEncrypted())) {
	    ret = PKCS7_BAD_CONTENT_TYPE;
	    goto end;
	}
	
	if( (store = makeStore(CAdir))==NULL) {
	    ret = PKCS7_STORE_ERROR;
	    goto end;
	}


	if( X509_cmp(signerCertif , extractedSignerCertif) != 0 ){
#ifdef DEBUG
 	    std::cout << extractedSigner.getHash() << std::endl;
 	    std::cout << signer.getHash() << std::endl;
#endif 
	    ret = PKCS7_BAD_SIGNER;
#ifdef DEBUG
 	    std::cout << "bad signer" << std::endl;
#endif 
	    goto end;
	}
	
	
	if(data) {
	    if(!(p7bio = BIO_new_file(data, "rb"))){
		ret =  PKCS7_BIO_NEW_ERROR;;
		goto end;
	    }
	}
	

	if(!data){
	    if(!isSignedAndEncrypted()) {
		p7bio = PKCS7_dataInit(p7,NULL);
	    } else {
		p7bio = PKCS7_dataDecode(p7,pkey,NULL, recipientCertif);
	    }
	}


	if(PKCS7_verify(p7, NULL, store , p7bio, NULL, PKCS7_NOVERIFY) != 1){
	    ret = PKCS7_SIGNATURE_VERIF_FAILED;
	}
	
    end:
	BIO_free(p7bio);
	X509_free(signerCertif);
	X509_free(extractedSignerCertif);
	X509_free(recipientCertif);
	X509_STORE_free(store);

	return ret;
    }




/* in progress *****

*/
    std::vector<Certificate> *pkcs7::getChainFromSigned(std::vector<Certificate> *CAList)
    {
	//get certificate from the pkcs7 
	std::vector<Certificate> vect = getCertificates();
	std::vector<Certificate>::iterator it = vect.begin();;
	std::vector<Certificate>::iterator CAit = CAList->begin();
	
	hash_set<Certificate, Certificate::Certificate_hash_str, Certificate::eqstr> CAhash;
	
	while(CAit != CAList->end()) {
	    CAhash.insert(*CAit);
	    CAit++;
	}
	

	//signer is the last element of the vector
	Certificate signerCert = *it;	
	while(it != vect.end()) {
	    CAhash.insert(*it);
	    signerCert = *it;
	    it++;
	}
#ifdef DEBUG
 	std::cout << "SIGNER :" << signerCert.getHash() << std::endl;
#endif 
	
	std::vector<Certificate> *CAmerged = new std::vector<Certificate>;
	hash_set<Certificate, Certificate::Certificate_hash_str, Certificate::eqstr>::iterator hashIt;
	
	for(hashIt = CAhash.begin() ; hashIt != CAhash.end() ; hashIt++) {
	    CAmerged->push_back(*hashIt);
	}
	
	std::vector<Certificate> *final = signerCert.getPathToRoot(CAmerged);
	CAmerged->clear();
	CAhash.clear();
	
	return final;
    }


    bool pkcs7::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) 
    {
	return true;
    }

    
    bool pkcs7::isSigned()
    { 
	return PKCS7_type_is_signed(p7)==1;
    }

    bool pkcs7::isEncrypted()
    {
	return (OBJ_obj2nid((p7)->type) ==  NID_pkcs7_enveloped);
    }

    bool pkcs7::isSignedAndEncrypted()
    { 
	return PKCS7_type_is_signedAndEnveloped(p7)==1;
	//(OBJ_obj2nid((p7)->type) ==  NID_pkcs7_signedAndEnveloped);
    }
  

    bool pkcs7::isDetached()
    {
	return PKCS7_is_detached(p7) == 1;
	//	return (p7->d.sign->contents->d.data == NULL);
    }



    std::vector<Certificate> &pkcs7::getCertificates()
    {
	STACK_OF(X509) *stack=sk_X509_new_null();
	X509 *cert=NULL;
	std::vector<Certificate> *certVect = new std::vector<Certificate>;
	
	// certs from  pkcs7_signed_st (see crypto/pkcs7/pkcs7.h)
	if(isSigned()) {
	    //stack of 509
	    stack = sk_X509_dup(p7->d.sign->cert);
	}
    
	//cert from pkcs7_signedandenveloped_st
	else if(isSignedAndEncrypted()) {
	    stack = sk_X509_dup(p7->d.sign->cert);
	}
    
	while((cert=sk_X509_pop(stack))!=NULL) {
	    Certificate *certif = new Certificate();
	    certif->setX509Certificate(cert);
	    //certif=cert;
	    certVect->push_back(*certif);    
	    // X509_free(cert); //pb with X509_dup ... it seems to not be a real dup
	} 
	sk_X509_free(stack);
	return *certVect;
    }
  
  
    std::vector<Certificate> &pkcs7::getRecipientsCertificates() 
    {
	STACK_OF(PKCS7_RECIP_INFO) *recipientStack = NULL;
	std::vector<Certificate> *certVect = new std::vector<Certificate>;
	X509 *cert=NULL;
	
	if(isEncrypted())
	    recipientStack = sk_PKCS7_RECIP_INFO_dup(p7->d.enveloped->recipientinfo);
    
	else if(isSignedAndEncrypted())
	    recipientStack = sk_PKCS7_RECIP_INFO_dup(p7->d.signed_and_enveloped->recipientinfo);
    
	while((cert=sk_PKCS7_RECIP_INFO_pop(recipientStack)->cert) != NULL) {
	    Certificate *certif = new Certificate();
	    certif->setX509Certificate(cert);
#ifdef DEBUG
 	    std::cout << certif->getHash() << std::endl;
#endif 
	    certVect->push_back(*certif);    
	    X509_free(cert);
	}

	sk_PKCS7_RECIP_INFO_free(recipientStack);
	return *certVect;
    }



    std::vector<Crl> &pkcs7::getCRL() 
    {
	STACK_OF(X509_CRL) *crlStack=NULL;
	std::vector<Crl> *crlVect = new std::vector<Crl>;
	X509_CRL *c = NULL;
	Crl *crl = NULL; 
    
	if(isSigned())
	    crlStack = sk_X509_CRL_dup(p7->d.sign->crl);
    
	else if(isSignedAndEncrypted())
	    crlStack = sk_X509_CRL_dup(p7->d.signed_and_enveloped->crl);
    
	while( (c = sk_X509_CRL_pop(crlStack)) !=NULL ){
	    crl = new Crl(c);
	    crlVect->push_back(*crl);
	}
    
	X509_CRL_free(c);
	sk_X509_CRL_free(crlStack);
	
	return *crlVect;
    }

 

    std::vector<PKCS7_RECIP_INFO> &pkcs7::getRecipientInfos()
    {
	std::vector<PKCS7_RECIP_INFO> *infos = new std::vector<PKCS7_RECIP_INFO>(0);

	if( p7 != NULL ) {
	    int i;
	    i=OBJ_obj2nid(p7->type);
	    STACK_OF(PKCS7_RECIP_INFO) *sk;
	    PKCS7_RECIP_INFO *recip = NULL;
	
	    switch (i) {
	    case NID_pkcs7_signedAndEnveloped:
		sk = sk_PKCS7_RECIP_INFO_dup(p7->d.signed_and_enveloped->recipientinfo);
		break;
	    case NID_pkcs7_enveloped:
		sk = sk_PKCS7_RECIP_INFO_dup(p7->d.enveloped->recipientinfo);
		break;
	    default:
		return *infos;
	    }
	
	    while( (recip = sk_PKCS7_RECIP_INFO_pop(sk)) !=NULL ) {
		infos->push_back(*recip);
	    }
	}
	return *infos;
     }



    Certificate& pkcs7::getSignerCertificate()
    {
	if( getCertificates().size() > 0 ){
	    Certificate *extractedSigner = new Certificate(getCertificates().back());
	    return *extractedSigner;
	} else {
	    Certificate *empty = new Certificate();
	    return *empty;
	}
    }

}
