<commit_before>/*
 * $Id$
 *
 * Copyright(C) 1998-2005 <NAME>
 * All rights reserved.
 *
 */

#include <qsconv.h>
#include <qsinit.h>
#include <qslog.h>
#include <qsstl.h>

#include <stdio.h>

#if !defined _WIN32_WCE || _WIN32_WCE > 300 || defined PLATFORM_PPC2002
#	include <tchar.h>
#	include <wincrypt.h>
#endif

#include <openssl/pem.h>

#include "crypto.h"
#include "smime.h"
#include "util.h"

using namespace qscrypto;
using namespace qs;


/****************************************************************************
 *
 * Global functions
 *
 */

extern "C" int passwordCallback(char* pBuf,
								int nSize,
								int nRWFlag,
								void* pParam);


/****************************************************************************
 *
 * NameImpl
 *
 */

qscrypto::NameImpl::NameImpl(X509_NAME* pName) :
	pName_(pName)
{
}

qscrypto::NameImpl::~NameImpl()
{
}

wstring_ptr qscrypto::NameImpl::getText() const
{
	char buf[1024];
	X509_NAME_oneline(pName_, buf, sizeof(buf) - 1);
	buf[sizeof(buf) - 1] = '\0';
	return mbs2wcs(buf);
}

wstring_ptr qscrypto::NameImpl::getCommonName() const
{
	return getText(NID_commonName);
}

wstring_ptr qscrypto::NameImpl::getEmailAddress() const
{
	return getText(NID_pkcs9_emailAddress);
}

wstring_ptr qscrypto::NameImpl::getText(int nid) const
{
	int nLen = X509_NAME_get_text_by_NID(pName_, nid, 0, 0);
	if (nLen == -1)
		return 0;
	
	string_ptr strText(allocString(nLen + 1));
	if (X509_NAME_get_text_by_NID(pName_, nid, strText.get(), nLen + 1) == -1)
		return 0;
	
	return mbs2wcs(strText.get());
}


/****************************************************************************
 *
 * GeneralNameImpl
 *
 */

qscrypto::GeneralNameImpl::GeneralNameImpl(GENERAL_NAME* pGeneralName) :
	pGeneralName_(pGeneralName)
{
}

qscrypto::GeneralNameImpl::~GeneralNameImpl()
{
}

qs::GeneralName::Type qscrypto::GeneralNameImpl::getType() const
{
	switch (pGeneralName_->type) {
	case GEN_DNS:
		return TYPE_DNS;
	case GEN_EMAIL:
		return TYPE_EMAIL;
	default:
		return TYPE_OTHER;
	}
}

wstring_ptr qscrypto::GeneralNameImpl::getValue() const
{
	switch (pGeneralName_->type) {
	case GEN_DNS:
		return mbs2wcs(reinterpret_cast<char*>(pGeneralName_->d.dNSName->data),
			pGeneralName_->d.dNSName->length);
	case GEN_EMAIL:
		return mbs2wcs(reinterpret_cast<char*>(pGeneralName_->d.rfc822Name->data),
			pGeneralName_->d.rfc822Name->length);
	default:
		return 0;
	}
}


/****************************************************************************
 *
 * GeneralNamesImpl
 *
 */

qscrypto::GeneralNamesImpl::GeneralNamesImpl(GENERAL_NAMES* pGeneralNames) :
	pGeneralNames_(pGeneralNames)
{
}

qscrypto::GeneralNamesImpl::~GeneralNamesImpl()
{
	sk_GENERAL_NAME_pop_free(pGeneralNames_, GENERAL_NAME_free);
}

int qscrypto::GeneralNamesImpl::getCount() const
{
	return sk_GENERAL_NAME_num(pGeneralNames_);
}

std::auto_ptr<GeneralName> qscrypto::GeneralNamesImpl::getGeneralName(int nIndex) const
{
	GENERAL_NAME* pGeneralName = sk_GENERAL_NAME_value(pGeneralNames_, nIndex);
	return std::auto_ptr<GeneralName>(new GeneralNameImpl(pGeneralName));
}


/****************************************************************************
 *
 * CertificateImpl
 *
 */

qscrypto::CertificateImpl::CertificateImpl() :
	pX509_(0),
	bFree_(true)
{
}

qscrypto::CertificateImpl::CertificateImpl(X509* pX509) :
	pX509_(pX509),
	bFree_(false)
{
}

qscrypto::CertificateImpl::~CertificateImpl()
{
	if (bFree_ && pX509_)
		X509_free(pX509_);
}

X509* qscrypto::CertificateImpl::getX509() const
{
	return pX509_;
}

X509* qscrypto::CertificateImpl::releaseX509()
{
	X509* p = pX509_;
	pX509_ = 0;
	return p;
}

bool qscrypto::CertificateImpl::load(const WCHAR* pwszPath,
									 FileType type,
									 CryptoPasswordCallback* pCallback)
{
	assert(pwszPath);
	
	FileInputStream stream(pwszPath);
	if (!stream)
		return false;
	BufferedInputStream bufferedStream(&stream, false);
	
	return load(&bufferedStream, type, pCallback);
}

bool qscrypto::CertificateImpl::load(InputStream* pStream,
									 FileType type,
									 CryptoPasswordCallback* pCallback)
{
	assert(pStream);
	
	malloc_size_ptr<unsigned char> buf(Util::createBIOFromStream(pStream));
	if (!buf.get())
		return false;
	
	BIOPtr pIn(BIO_new_mem_buf(buf.get(), buf.size()));
	
	switch (type) {
	case FILETYPE_PEM:
		pX509_ = PEM_read_bio_X509(pIn.get(), 0,
			pCallback ? &passwordCallback : 0, pCallback);
		if (!pX509_)
			return false;
		break;
	default:
		assert(false);
		break;
	}
	
	return true;
}

wstring_ptr qscrypto::CertificateImpl::getText() const
{
	BIOPtr pOut(BIO_new(BIO_s_mem()));
	if (X509_print(pOut.get(), pX509_) == 0)
		return 0;
	
	char* pBuf = 0;
	int nLen = BIO_get_mem_data(pOut.get(), &pBuf);
	
	return mbs2wcs(pBuf, nLen);
}

std::auto_ptr<Name> qscrypto::CertificateImpl::getSubject() const
{
	X509_NAME* pX509Name = X509_get_subject_name(pX509_);
	if (!pX509Name)
		return std::auto_ptr<Name>(0);
	return std::auto_ptr<Name>(new NameImpl(pX509Name));
}

std::auto_ptr<Name> qscrypto::CertificateImpl::getIssuer() const
{
	X509_NAME* pX509Name = X509_get_issuer_name(pX509_);
	if (!pX509Name)
		return std::auto_ptr<Name>(0);
	return std::auto_ptr<Name>(new NameImpl(pX509Name));
}

std::auto_ptr<GeneralNames> qscrypto::CertificateImpl::getSubjectAltNames() const
{
	GENERAL_NAMES* pGeneralNames = static_cast<GENERAL_NAMES*>(
		X509_get_ext_d2i(pX509_, NID_subject_alt_name, 0, 0));
	if (!pGeneralNames)
		return std::auto_ptr<GeneralNames>();
	return std::auto_ptr<GeneralNames>(new GeneralNamesImpl(pGeneralNames));
}


/****************************************************************************
 *
 * PrivateKeyImpl
 *
 */

qscrypto::PrivateKeyImpl::PrivateKeyImpl() :
	pKey_(0)
{
}

qscrypto::PrivateKeyImpl::~PrivateKeyImpl()
{
	if (pKey_)
		EVP_PKEY_free(pKey_);
}

EVP_PKEY* qscrypto::PrivateKeyImpl::getKey() const
{
	return pKey_;
}

bool qscrypto::PrivateKeyImpl::load(const WCHAR* pwszPath,
									FileType type,
									CryptoPasswordCallback* pCallback)
{
	assert(pwszPath);
	
	FileInputStream stream(pwszPath);
	if (!stream)
		return false;
	BufferedInputStream bufferedStream(&stream, false);
	
	return load(&bufferedStream, type, pCallback);
}

bool qscrypto::PrivateKeyImpl::load(InputStream* pStream,
									FileType type,
									CryptoPasswordCallback* pCallback)
{
	assert(pStream);
	
	malloc_size_ptr<unsigned char> buf(Util::createBIOFromStream(pStream));
	if (!buf.get())
		return false;
	
	BIOPtr pIn(BIO_new_mem_buf(buf.get(), buf.size()));
	
	switch (type) {
	case FILETYPE_PEM:
		pKey_ = PEM_read_bio_PrivateKey(pIn.get(), 0,
			pCallback ? &passwordCallback : 0, pCallback);
		if (!pKey_)
			return false;
		break;
	default:
		assert(false);
		break;
	}
	
	return true;
}


/****************************************************************************
 *
 * PublicKeyImpl
 *
 */

qscrypto::PublicKeyImpl::PublicKeyImpl() :
	pKey_(0)
{
}

qscrypto::PublicKeyImpl::~PublicKeyImpl()
{
	if (pKey_)
		EVP_PKEY_free(pKey_);
}

EVP_PKEY* qscrypto::PublicKeyImpl::getKey() const
{
	return pKey_;
}

bool qscrypto::PublicKeyImpl::load(const WCHAR* pwszPath,
								   FileType type,
								   CryptoPasswordCallback* pCallback)
{
	assert(pwszPath);
	
	FileInputStream stream(pwszPath);
	if (!stream)
		return false;
	BufferedInputStream bufferedStream(&stream, false);
	
	return load(&bufferedStream, type, pCallback);
}

bool qscrypto::PublicKeyImpl::load(InputStream* pStream,
								   FileType type,
								   CryptoPasswordCallback* pCallback)
{
	assert(pStream);
	
	malloc_size_ptr<unsigned char> buf(Util::createBIOFromStream(pStream));
	if (!buf.get())
		return false;
	
	BIOPtr pIn(BIO_new_mem_buf(buf.get(), buf.size()));
	
	switch (type) {
	case FILETYPE_PEM:
		pKey_ = PEM_read_bio_PUBKEY(pIn.get(), 0,
			pCallback ? &passwordCallback : 0, pCallback);
		if (!pKey_)
			return false;
		break;
	default:
		assert(false);
		break;
	}
	
	return true;
}


/****************************************************************************
 *
 * StoreImpl
 *
 */

qscrypto::StoreImpl::StoreImpl() :
	pStore_(0)
{
	pStore_ = X509_STORE_new();
	if (!pStore_)
		return;
}

qscrypto::StoreImpl::~StoreImpl()
{
	if (pStore_)
		X509_STORE_free(pStore_);
}

X509_STORE* qscrypto::StoreImpl::getStore() const
{
	return pStore_;
}

bool qscrypto::StoreImpl::load(const WCHAR* pwszPath,
							   FileType type)
{
	assert(pwszPath);
	
	Log log(InitThread::getInitThread().getLogger(), L"qscrypto::StoreImpl");
	
#if 1
	switch (type) {
	case FILETYPE_PEM:
		{
			FileInputStream stream(pwszPath);
			if (!stream)
				return false;
			BufferedInputStream bufferedStream(&stream, false);
			
			malloc_size_ptr<unsigned char> buf(Util::createBIOFromStream(&bufferedStream));
			if (!buf.get())
				return false;
			
			BIOPtr pIn(BIO_new_mem_buf(buf.get(), buf.size()));
			
			STACK_OF(X509_INFO)* pStackInfo =
				PEM_X509_INFO_read_bio(pIn.get(), 0, 0, 0);
			if (!pStackInfo)
				return false;
			for (int n = 0; n < sk_X509_INFO_num(pStackInfo); ++n) {
				X509_INFO* pInfo = sk_X509_INFO_value(pStackInfo, n);
				if (pInfo->x509)
					X509_STORE_add_cert(pStore_, pInfo->x509);
				if (pInfo->crl)
					X509_STORE_add_crl(pStore_, pInfo->crl);
			}
			sk_X509_INFO_pop_free(pStackInfo, X509_INFO_free);
		}
		break;
	default:
		assert(false);
		break;
	}
#else
	string_ptr strPath(wcs2mbs(pwszPath));
	
	X509_LOOKUP* pLookup = X509_STORE_add_lookup(pStore_, X509_LOOKUP_file());
	if (!pLookup) {
		Util::logError(log, L"X509_STORE_add_lookup failed");
		return false;
	}
	switch (type) {
	case FILETYPE_PEM:
		{
			int n = X509_LOOKUP_load_file(pLookup,
				strPath.get(), X509_FILETYPE_PEM);
			if (!n) {
				Util::logError(log, L"X509_LOOKUP_load_file failed");
				return false;
			}
		}
		break;
	default:
		assert(false);
		break;
	}
#endif
	
	return true;
}

bool qscrypto::StoreImpl::loadSystem()
{
#if !defined _WIN32_WCE || _WIN32_WCE > 300 || defined PLATFORM_PPC2002
	HCERTSTORE hStore = ::CertOpenSystemStore(0, _T("ROOT"));
	if (!hStore)
		return false;
	
	const CERT_CONTEXT* pContext = 0;
	while (true) {
		pContext = ::CertEnumCertificatesInStore(hStore, pContext);
		if (!pContext)
			break;
		
		unsigned char* p = pContext->pbCertEncoded;
		X509Ptr x509(d2i_X509(0, &p, pContext->cbCertEncoded));
		if (x509.get())
			X509_STORE_add_cert(pStore_, x509.get());
	}
	::CertFreeCertificateContext(pContext);
	::CertCloseStore(hStore, 0);<commit_msg>Load system's CA store in addition to ROOT store. <BTS:929><commit_after>/*
 * $Id$
 *
 * Copyright(C) 1998-2005 <NAME>
 * All rights reserved.
 *
 */

#include <qsconv.h>
#include <qsinit.h>
#include <qslog.h>
#include <qsstl.h>

#include <stdio.h>

#if !defined _WIN32_WCE || _WIN32_WCE > 300 || defined PLATFORM_PPC2002
#	include <tchar.h>
#	include <wincrypt.h>
#endif

#include <openssl/pem.h>

#include "crypto.h"
#include "smime.h"
#include "util.h"

using namespace qscrypto;
using namespace qs;


/****************************************************************************
 *
 * Global functions
 *
 */

extern "C" int passwordCallback(char* pBuf,
								int nSize,
								int nRWFlag,
								void* pParam);


/****************************************************************************
 *
 * NameImpl
 *
 */

qscrypto::NameImpl::NameImpl(X509_NAME* pName) :
	pName_(pName)
{
}

qscrypto::NameImpl::~NameImpl()
{
}

wstring_ptr qscrypto::NameImpl::getText() const
{
	char buf[1024];
	X509_NAME_oneline(pName_, buf, sizeof(buf) - 1);
	buf[sizeof(buf) - 1] = '\0';
	return mbs2wcs(buf);
}

wstring_ptr qscrypto::NameImpl::getCommonName() const
{
	return getText(NID_commonName);
}

wstring_ptr qscrypto::NameImpl::getEmailAddress() const
{
	return getText(NID_pkcs9_emailAddress);
}

wstring_ptr qscrypto::NameImpl::getText(int nid) const
{
	int nLen = X509_NAME_get_text_by_NID(pName_, nid, 0, 0);
	if (nLen == -1)
		return 0;
	
	string_ptr strText(allocString(nLen + 1));
	if (X509_NAME_get_text_by_NID(pName_, nid, strText.get(), nLen + 1) == -1)
		return 0;
	
	return mbs2wcs(strText.get());
}


/****************************************************************************
 *
 * GeneralNameImpl
 *
 */

qscrypto::GeneralNameImpl::GeneralNameImpl(GENERAL_NAME* pGeneralName) :
	pGeneralName_(pGeneralName)
{
}

qscrypto::GeneralNameImpl::~GeneralNameImpl()
{
}

qs::GeneralName::Type qscrypto::GeneralNameImpl::getType() const
{
	switch (pGeneralName_->type) {
	case GEN_DNS:
		return TYPE_DNS;
	case GEN_EMAIL:
		return TYPE_EMAIL;
	default:
		return TYPE_OTHER;
	}
}

wstring_ptr qscrypto::GeneralNameImpl::getValue() const
{
	switch (pGeneralName_->type) {
	case GEN_DNS:
		return mbs2wcs(reinterpret_cast<char*>(pGeneralName_->d.dNSName->data),
			pGeneralName_->d.dNSName->length);
	case GEN_EMAIL:
		return mbs2wcs(reinterpret_cast<char*>(pGeneralName_->d.rfc822Name->data),
			pGeneralName_->d.rfc822Name->length);
	default:
		return 0;
	}
}


/****************************************************************************
 *
 * GeneralNamesImpl
 *
 */

qscrypto::GeneralNamesImpl::GeneralNamesImpl(GENERAL_NAMES* pGeneralNames) :
	pGeneralNames_(pGeneralNames)
{
}

qscrypto::GeneralNamesImpl::~GeneralNamesImpl()
{
	sk_GENERAL_NAME_pop_free(pGeneralNames_, GENERAL_NAME_free);
}

int qscrypto::GeneralNamesImpl::getCount() const
{
	return sk_GENERAL_NAME_num(pGeneralNames_);
}

std::auto_ptr<GeneralName> qscrypto::GeneralNamesImpl::getGeneralName(int nIndex) const
{
	GENERAL_NAME* pGeneralName = sk_GENERAL_NAME_value(pGeneralNames_, nIndex);
	return std::auto_ptr<GeneralName>(new GeneralNameImpl(pGeneralName));
}


/****************************************************************************
 *
 * CertificateImpl
 *
 */

qscrypto::CertificateImpl::CertificateImpl() :
	pX509_(0),
	bFree_(true)
{
}

qscrypto::CertificateImpl::CertificateImpl(X509* pX509) :
	pX509_(pX509),
	bFree_(false)
{
}

qscrypto::CertificateImpl::~CertificateImpl()
{
	if (bFree_ && pX509_)
		X509_free(pX509_);
}

X509* qscrypto::CertificateImpl::getX509() const
{
	return pX509_;
}

X509* qscrypto::CertificateImpl::releaseX509()
{
	X509* p = pX509_;
	pX509_ = 0;
	return p;
}

bool qscrypto::CertificateImpl::load(const WCHAR* pwszPath,
									 FileType type,
									 CryptoPasswordCallback* pCallback)
{
	assert(pwszPath);
	
	FileInputStream stream(pwszPath);
	if (!stream)
		return false;
	BufferedInputStream bufferedStream(&stream, false);
	
	return load(&bufferedStream, type, pCallback);
}

bool qscrypto::CertificateImpl::load(InputStream* pStream,
									 FileType type,
									 CryptoPasswordCallback* pCallback)
{
	assert(pStream);
	
	malloc_size_ptr<unsigned char> buf(Util::createBIOFromStream(pStream));
	if (!buf.get())
		return false;
	
	BIOPtr pIn(BIO_new_mem_buf(buf.get(), buf.size()));
	
	switch (type) {
	case FILETYPE_PEM:
		pX509_ = PEM_read_bio_X509(pIn.get(), 0,
			pCallback ? &passwordCallback : 0, pCallback);
		if (!pX509_)
			return false;
		break;
	default:
		assert(false);
		break;
	}
	
	return true;
}

wstring_ptr qscrypto::CertificateImpl::getText() const
{
	BIOPtr pOut(BIO_new(BIO_s_mem()));
	if (X509_print(pOut.get(), pX509_) == 0)
		return 0;
	
	char* pBuf = 0;
	int nLen = BIO_get_mem_data(pOut.get(), &pBuf);
	
	return mbs2wcs(pBuf, nLen);
}

std::auto_ptr<Name> qscrypto::CertificateImpl::getSubject() const
{
	X509_NAME* pX509Name = X509_get_subject_name(pX509_);
	if (!pX509Name)
		return std::auto_ptr<Name>(0);
	return std::auto_ptr<Name>(new NameImpl(pX509Name));
}

std::auto_ptr<Name> qscrypto::CertificateImpl::getIssuer() const
{
	X509_NAME* pX509Name = X509_get_issuer_name(pX509_);
	if (!pX509Name)
		return std::auto_ptr<Name>(0);
	return std::auto_ptr<Name>(new NameImpl(pX509Name));
}

std::auto_ptr<GeneralNames> qscrypto::CertificateImpl::getSubjectAltNames() const
{
	GENERAL_NAMES* pGeneralNames = static_cast<GENERAL_NAMES*>(
		X509_get_ext_d2i(pX509_, NID_subject_alt_name, 0, 0));
	if (!pGeneralNames)
		return std::auto_ptr<GeneralNames>();
	return std::auto_ptr<GeneralNames>(new GeneralNamesImpl(pGeneralNames));
}


/****************************************************************************
 *
 * PrivateKeyImpl
 *
 */

qscrypto::PrivateKeyImpl::PrivateKeyImpl() :
	pKey_(0)
{
}

qscrypto::PrivateKeyImpl::~PrivateKeyImpl()
{
	if (pKey_)
		EVP_PKEY_free(pKey_);
}

EVP_PKEY* qscrypto::PrivateKeyImpl::getKey() const
{
	return pKey_;
}

bool qscrypto::PrivateKeyImpl::load(const WCHAR* pwszPath,
									FileType type,
									CryptoPasswordCallback* pCallback)
{
	assert(pwszPath);
	
	FileInputStream stream(pwszPath);
	if (!stream)
		return false;
	BufferedInputStream bufferedStream(&stream, false);
	
	return load(&bufferedStream, type, pCallback);
}

bool qscrypto::PrivateKeyImpl::load(InputStream* pStream,
									FileType type,
									CryptoPasswordCallback* pCallback)
{
	assert(pStream);
	
	malloc_size_ptr<unsigned char> buf(Util::createBIOFromStream(pStream));
	if (!buf.get())
		return false;
	
	BIOPtr pIn(BIO_new_mem_buf(buf.get(), buf.size()));
	
	switch (type) {
	case FILETYPE_PEM:
		pKey_ = PEM_read_bio_PrivateKey(pIn.get(), 0,
			pCallback ? &passwordCallback : 0, pCallback);
		if (!pKey_)
			return false;
		break;
	default:
		assert(false);
		break;
	}
	
	return true;
}


/****************************************************************************
 *
 * PublicKeyImpl
 *
 */

qscrypto::PublicKeyImpl::PublicKeyImpl() :
	pKey_(0)
{
}

qscrypto::PublicKeyImpl::~PublicKeyImpl()
{
	if (pKey_)
		EVP_PKEY_free(pKey_);
}

EVP_PKEY* qscrypto::PublicKeyImpl::getKey() const
{
	return pKey_;
}

bool qscrypto::PublicKeyImpl::load(const WCHAR* pwszPath,
								   FileType type,
								   CryptoPasswordCallback* pCallback)
{
	assert(pwszPath);
	
	FileInputStream stream(pwszPath);
	if (!stream)
		return false;
	BufferedInputStream bufferedStream(&stream, false);
	
	return load(&bufferedStream, type, pCallback);
}

bool qscrypto::PublicKeyImpl::load(InputStream* pStream,
								   FileType type,
								   CryptoPasswordCallback* pCallback)
{
	assert(pStream);
	
	malloc_size_ptr<unsigned char> buf(Util::createBIOFromStream(pStream));
	if (!buf.get())
		return false;
	
	BIOPtr pIn(BIO_new_mem_buf(buf.get(), buf.size()));
	
	switch (type) {
	case FILETYPE_PEM:
		pKey_ = PEM_read_bio_PUBKEY(pIn.get(), 0,
			pCallback ? &passwordCallback : 0, pCallback);
		if (!pKey_)
			return false;
		break;
	default:
		assert(false);
		break;
	}
	
	return true;
}


/****************************************************************************
 *
 * StoreImpl
 *
 */

qscrypto::StoreImpl::StoreImpl() :
	pStore_(0)
{
	pStore_ = X509_STORE_new();
	if (!pStore_)
		return;
}

qscrypto::StoreImpl::~StoreImpl()
{
	if (pStore_)
		X509_STORE_free(pStore_);
}

X509_STORE* qscrypto::StoreImpl::getStore() const
{
	return pStore_;
}

bool qscrypto::StoreImpl::load(const WCHAR* pwszPath,
							   FileType type)
{
	assert(pwszPath);
	
	Log log(InitThread::getInitThread().getLogger(), L"qscrypto::StoreImpl");
	
#if 1
	switch (type) {
	case FILETYPE_PEM:
		{
			FileInputStream stream(pwszPath);
			if (!stream)
				return false;
			BufferedInputStream bufferedStream(&stream, false);
			
			malloc_size_ptr<unsigned char> buf(Util::createBIOFromStream(&bufferedStream));
			if (!buf.get())
				return false;
			
			BIOPtr pIn(BIO_new_mem_buf(buf.get(), buf.size()));
			
			STACK_OF(X509_INFO)* pStackInfo =
				PEM_X509_INFO_read_bio(pIn.get(), 0, 0, 0);
			if (!pStackInfo)
				return false;
			for (int n = 0; n < sk_X509_INFO_num(pStackInfo); ++n) {
				X509_INFO* pInfo = sk_X509_INFO_value(pStackInfo, n);
				if (pInfo->x509)
					X509_STORE_add_cert(pStore_, pInfo->x509);
				if (pInfo->crl)
					X509_STORE_add_crl(pStore_, pInfo->crl);
			}
			sk_X509_INFO_pop_free(pStackInfo, X509_INFO_free);
		}
		break;
	default:
		assert(false);
		break;
	}
#else
	string_ptr strPath(wcs2mbs(pwszPath));
	
	X509_LOOKUP* pLookup = X509_STORE_add_lookup(pStore_, X509_LOOKUP_file());
	if (!pLookup) {
		Util::logError(log, L"X509_STORE_add_lookup failed");
		return false;
	}
	switch (type) {
	case FILETYPE_PEM:
		{
			int n = X509_LOOKUP_load_file(pLookup,
				strPath.get(), X509_FILETYPE_PEM);
			if (!n) {
				Util::logError(log, L"X509_LOOKUP_load_file failed");
				return false;
			}
		}
		break;
	default:
		assert(false);
		break;
	}
#endif
	
	return true;
}

bool qscrypto::StoreImpl::loadSystem()
{
#if !defined _WIN32_WCE || _WIN32_WCE > 300 || defined PLATFORM_PPC2002
	const TCHAR* ptszNames[] = {
		_T("ROOT"),
		_T("CA")
	};
	for (int n = 0; n < countof(ptszNames); ++n) {
		HCERTSTORE hStore = ::CertOpenSystemStore(0, ptszNames[n]);
		if (!hStore)
			return false;
		
		const CERT_CONTEXT* pContext = 0;
		while (true) {
			pContext = ::CertEnumCertificatesInStore(hStore, pContext);
			if (!pContext)
				break;
			
			unsigned char* p = pContext->pbCertEncoded;
			X509Ptr x509(d2i_X509(0, &p, pContext->cbCertEncoded));
			if (x509.get())
				X509_STORE_add_cert(pStore_, x509.get());
		}
		::CertFreeCertificateContext(pContext);
		::CertCloseStore(hStore, 0);
	}
{'fraction_non_alphanumeric': 0.18975286891100424, 'fraction_numerical': 0.027427265368315794, 'mean_word_length': 5.3971976850441665, 'pattern_counts': {'":': 0, '<': 61, '<?xml version=': 0, '>': 94, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1522653', 'n_tokens_mistral': 8841, 'n_tokens_neox': 7757, 'n_words': 1306}