/*
 * Copyright(C) 2003 Проект ИОК
 *
 * Этот файл содержит информацию, являющуюся
 * собственностью компании Крипто Про.
 *
 * Любая часть этого файла не может быть скопирована,
 * исправлена, переведена на другие языки,
 * локализована или модифицирована любым способом,
 * откомпилирована, передана по сети с или на
 * любую компьютерную систему без предварительного
 * заключения соглашения с компанией Крипто Про.
 */

/*!
 * \file $RCSfile: PKIXCMP_Base.h,v $
 * \version $Revision: 1.48 $
 * \date $Date: 2005/01/24 12:36:43 $
 * \author $Author: cav $
 *
 * \brief PKIXCMP_Base class
 */

#ifndef PKIXCMP_BASE_H
#define PKIXCMP_BASE_H

#include "CA_CMP_Types.h"
#include "CA_CMP_Msg.h"
#include "CA_CMP_Certificate.h"
#include "CA_CMP_Requests.h"
#include "CA_CMP_ErrorMsg.h"
#include "CA_CMP_CRL.h"
#include "CA_CMP_RevAnnContent.h"
#include "StoreUtil.h"

typedef std::pair<std::string,std::string> ReaderNamePair;
// Первый элемент пары - понятное имя (русифицированное) считывателя
// Второй элемент пары - имя считывателя, так как оно используется
//  в CSP

// Список имен считывателей
typedef std::list< ReaderNamePair > ReaderList;

class Retry {
public:
    virtual ~Retry() {}
    bool operator()( void ) 
    { 
	if( call() ) { err = 0; return true; }
	err = GetLastError(); 
	return is_exit(); 
    }
    DWORD LastError() const { return err; }
    bool is_exit() const { return !err 
	|| err == (DWORD)SCARD_W_CANCELLED_BY_USER
	|| err == (DWORD)SCARD_W_WRONG_CHV
	|| err == (DWORD)SCARD_E_INVALID_CHV
#ifdef _WIN32
	|| err == (DWORD)NTE_BAD_TYPE  //for Prov params not supported by RSA 
#endif
	|| err == (DWORD)SCARD_W_CHV_BLOCKED; }
protected:
    virtual BOOL call() = 0;
    DWORD err;
};

class WndProv {
public:
    virtual ~WndProv() {}
    // Оконная функция с просьбой вставить носитель для создания контейнера
    // с именем name.
    // \param name [in] имя контейнера, который необходимо создать
    // \param error [in] Номер последней ошибки 0 для первого вызова
    // \param n [in] Количество создаваемых носителей
    // \param num [in] Номер создаваемого носителя
    virtual bool insert_create( const char *name, unsigned long error, 
	short n, int num ) = 0;

    // Оконная функция повтора операции.
    // Окно висит до тех пор пока успешно не завершилась операции
    // или пользователь не нажал CANCEL. Функция должна выполнить 
    // функцию и только после неуспеха выводить окно
    // \param name [in] Имя носителя
    // \param num [in] Номер носителя или -1 для носителей на диске
    // \param retry Функция повтора
    virtual bool retry( const char *name, int num, Retry &retry ) = 0;

    // Собственно оконная часть запроса pin-кода
    // \param name [in] Имя контейнера
    // \param i [in] количенство оставшихся попыток ввода pin. При первом вызове
    //  == 3. 
    // \param pin [out] pin-код состоит не более чем из 8 символов, допускается 
    //   пустой pin
    // \param n [in] Количество возможных носителей
    // \param num [in] Номер прогружаемой части
    // \return false пользователь отказался от ввода pin. true Ok
    virtual bool get_current_pin( const char *name, short i, char pin[9], short n, 
	int num  ) = 0;

    // Оконная часть запроса на новый pin-код
    // \param name [in] Имя контейнера
    // \param pin [out] pin-код состоит не более чем из 8 символов, допускается 
    //   пустой pin
    // \param n [in] Количество возможных носителей
    // \param i [in] Номер прогружаемой части
    // \return false пользователь отказался от ввода pin. true Ok
    virtual bool get_new_pin( const char *name, char pin[9], short n, 
	int num  ) = 0;

    // Функция запроса на какой считыватель создавать контейнер
    // \param reader_list [in] список возможных считывателей
    // \return имя выбранного считывателя
    virtual std::string get_reader( const ReaderList& ) = 0;

    // Возвращает размер ключа
    virtual int get_key_size();

    // Если возвращает true, включается режим использования встроенного 
    // оконного интерфейса провайдера вместо WndProv
    virtual bool use_builtin_windows ();

    // Если возвращается true то пин не устанавливается (даже пустой)
    virtual bool no_pin();

    // Если возвращается true то на новые контейнеры пин устанавливается насильно
    virtual bool force_new_pin();

protected:
    std::string m_cached_current_pin; // used by PkiClient
    bool m_using_cached_current_pin; // used by PkiClient
};

class OldWndProv : public WndProv
{
public:
    OldWndProv( const char *szContainer, const char *pin );
    OldWndProv( const std::string &reader, const char *pin );
    // Возвращает размер ключа
    virtual int get_key_size()
    {
      return 0;
    }
    virtual bool insert_create( const char *, unsigned long error, short, int ) 
    {
	if( error )
	    throw CA_CRYPT_EXCEPTION;
	return true;
    }
    virtual bool retry( const char *, int, Retry &a_retry )
    { return a_retry(); }
    virtual bool get_current_pin( const char *, short i, char pin[9], short, int)
    {
	if( i != 3 )
	    throw CA_CRYPT_EXCEPTION;
	strcpy( pin, pin_ );
	return true;
    }
    virtual bool get_new_pin( const char *, char pin[9], short, 
	int )
    { strcpy( pin, pin_ ); return true; }
    virtual std::string get_reader( const ReaderList& )
    { return reader_; }

    char pin_[9];
    std::string reader_;
};

#if defined _WIN32
class WIN32WndProv : public WndProv
{
public:
    WIN32WndProv () {}
    // Возвращает размер ключа
    virtual int get_key_size();
    virtual bool use_builtin_windows ();

    virtual bool insert_create( const char *name, unsigned long error, 
	short n, int num );
    virtual bool retry( const char *name, int num, Retry &retry );
    virtual bool get_current_pin( const char *name, short i, char pin[9], short n, 
	int num  );
    virtual bool get_new_pin( const char *name, char pin[9], short n, 
	int num  );
    virtual std::string get_reader( const ReaderList& );
};
#endif	/* _WIN32 */

class PKIXCMP_Base {
public:
    static CACMPT_BLOB GetTLSCert(const char *ini_path);
    static void SetTLSCert(const char *ini_path, const CACMPT_BLOB& cert);

    // Добавить сертификат в store
    static void CertStore_Add(const wchar_t *store_name, const CACMPT_BLOB &cert, bool SystemStore);
    // Добавить сертификат в store с использованием структуры
    static void CertStore_Add(const CertificateStore & store, const CACMPT_BLOB &cert)
    {
	CertStore_Add(store.GetName(), cert, store.isSystem());
    }

    // Добавить crl в store
    static void CrlStore_Add(const wchar_t *store_name,
	const CACMPT_BLOB &crl, bool SystemStore = true, bool replace = true);

    // Добавить crl в store c использованием структуры
    static void CrlStore_Add(const CertificateStore & store,
	const CACMPT_BLOB &crl, bool replace = true )
    {
	    CrlStore_Add(store.GetName(), crl, store.isSystem(), replace);
    };

  // Найти сертификат в store по подстроке Issuer и серийному номеру
    static void CertStore_Find(encoded_certificate_list &dest,
	const wchar_t *lpwStoreName,
	const wchar_t *issuer,
	const char *serial, bool SystemStore);
    //Найти сертификат в store по подстроке Issuer и серийному номеру
    static void CertStore_Find(encoded_certificate_list &dest,
			       const CertificateStore &store,
			       const wchar_t *issuer,
			       const char *serial)
    {
	CertStore_Find(dest, store.GetName(), issuer, serial, store.isSystem());
    };

    // эти две оставлены на случай, если используются в Стандарте или Службах
    // Найти сертификат в store по Subject и серийному номеру
    static void CertStore_Find(encoded_certificate_list &dest,
			       const wchar_t *lpwStoreName,
			       const CACMPT_BLOB &subject,
			       const char *serial, bool SystemStore);
    // Найти сертификат в store по Subject и серийному номеру
    static void CertStore_Find(encoded_certificate_list &dest,
			       const CertificateStore &store,
			       const CACMPT_BLOB &subject,
			       const char *serial)
    {
	CertStore_Find(dest, store.GetName(), subject, serial, store.isSystem());
    };

    // Найти сертификат в store по открытому ключу.
    static void CertStore_Find(encoded_certificate_list &dest,
			      const wchar_t *lpwStoreName,
			      CACMPT_PublicKeyInfo &public_info, bool SystemStore);
    // Найти сертификат в store по открытому ключу
    static void CertStore_Find(encoded_certificate_list &dest,
			       const CertificateStore & store,
			       CACMPT_PublicKeyInfo &public_info)
    {
	CertStore_Find(dest, store.GetName(), public_info, store.isSystem());
    };

    // Проверить наличие сертификата в store
    static bool CertStore_Is( const CACMPT_BLOB &cert,
	const wchar_t *lpwStoreName, bool SystemStore = true );
    // Проверить наличие сертификата в store
    static bool CertStore_Is( const CACMPT_BLOB &cert,
	const CertificateStore & store)
    {
	    return CertStore_Is(cert,store.GetName(),store.isSystem());
    }

    // Найти crl в store по подстроке Issuer
    static void CrlStore_Find(encoded_crl_list &dest,
	const wchar_t *lpwStoreName,
	const wchar_t *issuer, bool SystemStore);

    // Найти crl в store по подстроке Issuer
    static void CrlStore_Find(encoded_crl_list &dest,
	const CertificateStore & store, const wchar_t *issuer)
    {
	CrlStore_Find(dest, store.GetName(), issuer, store.isSystem());
    };

    // Найти crl в store по Issuer
    static void CrlStore_Find(encoded_crl_list &dest,
	const wchar_t *lpwStoreName,
	const CACMPT_BLOB &issuer, bool SystemStore = true );
    // Найти crl в store по Issuer
    static void CrlStore_Find(encoded_crl_list &dest,
	const CertificateStore & store,
	const CACMPT_BLOB &issuer)
    {
	    CrlStore_Find(dest, store.GetName(), issuer, store.isSystem());
    };

    // Удалить сертификат из store
    static int CertStore_Del( const wchar_t *store_name,
	const CACMPT_BLOB &cert, bool SystemStore = true );
    // Удалить сертификат из store
    static int CertStore_Del( const CertificateStore & store,
	const CACMPT_BLOB &cert)
    {
	return CertStore_Del(store.GetName(), cert, store.isSystem());
    };

    // Удалить crl из store
    static void CrlStore_Del(const wchar_t *store_name, const CACMPT_BLOB &crl, bool SystemStore);
    // Удалить crl из store
    static void CrlStore_Del(const CertificateStore &store, const CACMPT_BLOB &crl)
    {
	CrlStore_Del(store.GetName(), crl, store.isSystem());
    }

    // декодирование сертификата
    static void Cert_Decode(const CACMPT_BLOB &c_msg, CertificateInfo *info);
    // декодирование запроса в структуру CertificateInfo (для сохранения в БД)
    static void CertRepMessage_Decode(const CACMPT_BLOB &cp_msg, CertificateInfo *info);

    // декодирование файла crl
    static void Crl_Decode(const CACMPT_BLOB &crl_msg, CrlInfo *info);

    // декодирование запроса в структуру RequestInfo (для сохранения в БД, ...)
    static int CertReqMessage_Decode(const CACMPT_BLOB &cr_msg, 
	RequestInfo *info);

    static void InstallRoot(const CACMPT_BLOB &cert)
    {
	CertStore_Add(Store_Root, cert);
    }

    static void ErrorMsgInfo_Decode(const CACMPT_BLOB &em_msg, ErrorMsgInfo *em_info);

    // Функция проверки цепочек
    // \param cert [in] Проверяемый сертификат
    // \param encoded_certificate_list [in] список доп сертификатов
    // \param add_store [in] имена доп. store в который необходимо
    //  искать сертификаты
    // \param add_store_len [in] количество доп store
    static unsigned CertChain_VerifyCertificate( const CACMPT_BLOB &cert,
	encoded_certificate_list &path,
	const encoded_certificate_list *cert_list,
	const CertificateStore *add_store = NULL, int add_store_len = 0);

    // Функция проверки
    static unsigned CertChain_VerifyCMPMessage(
	const CACMPT_BLOB &msg,
	const CertificateStore *add_store, int add_store_len,
	encoded_certificate_list *path);

    // проверка содержимого и подписи полученного запроса
    // @parm cr_msg | запрос на сертификат
    // @parm ignoreMsgSignature | FALSE - игнорировать общую подпись сообщения
    // необходимо в случаях, когда подпись PKCS-запроса и подпись собщения различаются
    // например: PKCS10-запрос будет подписан непосредственно новым запрашиваемым ключом,
    // а само PKICMP-сообщение будет подписано УЛ
    // 
    // Чудов: Неправда!!! эти две подписи давно уже проверяются независимо
    unsigned CertReqMessage_Verify(const CACMPT_BLOB &cr_msg,
				   encoded_certificate_list *path = NULL);

    static unsigned CertChain_VerifyCrl( const CACMPT_BLOB &crl,
	const CertificateStore *add_store, int add_store_len);

    static void InstallCertificateToStore( const CACMPT_BLOB &RawCertificate,
	HCRYPTPROV hProv, DWORD dwKeySpec, 
	const wchar_t * lpwStoreName = L"My", 
	DWORD dwOpenStore = CERT_SYSTEM_STORE_CURRENT_USER );

    static void InstallCertificateToStore( const CACMPT_BLOB &RawCertificate,
	HCRYPTPROV hProv, DWORD dwKeySpec, 
	const CertificateStore store) 
    {
	    InstallCertificateToStore(RawCertificate,hProv,dwKeySpec,store.GetName(),
			    (store.isSystem()? CERT_SYSTEM_STORE_LOCAL_MACHINE : CERT_SYSTEM_STORE_CURRENT_USER ));
    };

    void CreateTemplate (RequestInfo *info, DWORD dwOpenStore = CERT_SYSTEM_STORE_CURRENT_USER);

    int PKCS10_EncodeAndSign(RequestInfo *info, CACMPT_BLOB &cr_msg, bool fInstallAsMy);

    static std::wstring ChainStatusToString( unsigned chain_status );

    virtual const CertificateStore * Get_ADD_STORE() const =0;
    virtual int Get_ADD_STORE_LEN () const = 0;

    // rsv: public to give access to QuickCert
    static void InstallCertificateToContainer( const CACMPT_BLOB &RawCertificate,
	HCRYPTPROV hProv, DWORD dwKeySpec );

protected:
    PKIXCMP_Base();
    virtual ~PKIXCMP_Base();

    DWORD dwKeySpec;
    DWORD dwProvType_;
    char * szProvider_;
    HCRYPTPROV hProv;
    CACMPT_BLOB MYCert;

    // Создать ErrorMsgInfo
    static void ErrorMsgInfo_EncodeAndSign(const ErrorMsgInfo *em_info,
	CACMPT_BLOB &em_msg, unsigned long hProv, unsigned long dwKeySpec,
	CACMPT_BLOB &cert, bool ToSign=true);
    static void GenerateKeyPair(WndProv &wnd_prov,
	const char *szProvider, int dwProvType,
	const char *reader, const char *container,
	unsigned long keySpec, bool keyExportable,
        HCRYPTPROV &hProv);

    static unsigned ErrorMsgInfo_Verify( const CACMPT_BLOB &em_msg,
	const CertificateStore *add_store, int add_store_len);
};
#endif // PKIXCMP_BASE_H
