/**
 * @file llsechandler_basic.h
 * @brief Security API for services such as certificate handling
 * secure local storage, etc.
 *
 * $LicenseInfo:firstyear=2009&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#ifndef LLSECHANDLER_BASIC
#define LLSECHANDLER_BASIC

#ifdef LL_WINDOWS
#pragma warning (push)
#pragma warning(disable:4250)
#endif // LL_WINDOWS

#include "llsecapi.h"
#include <vector>
#include <openssl/x509.h>

// helpers
extern LLSD cert_name_from_X509_NAME(X509_NAME* name);
extern std::string cert_string_name_from_X509_NAME(X509_NAME* name);
extern std::string cert_string_from_asn1_integer(ASN1_INTEGER* value);
extern LLDate cert_date_from_asn1_time(ASN1_TIME* asn1_time);
extern std::string cert_get_digest(const std::string& digest_type, X509 *cert);


// class LLCertificate
//
class LLBasicCertificate : public LLCertificate
{
public:
    LOG_CLASS(LLBasicCertificate);

    // The optional validation_params allow us to make the unit test time-invariant
    LLBasicCertificate(const std::string& pem_cert, const LLSD* validation_params = NULL);
    LLBasicCertificate(X509* openSSLX509, const LLSD* validation_params = NULL);

    virtual ~LLBasicCertificate();

    virtual std::string getPem() const;
    virtual std::vector<U8> getBinary() const;
    virtual void getLLSD(LLSD &llsd);

    virtual X509* getOpenSSLX509() const;

    // set llsd elements for testing
    void setLLSD(const std::string name, const LLSD& value) { mLLSDInfo[name] = value; }

protected:

    // certificates are stored as X509 objects, as validation and
    // other functionality is via openssl
    X509* mCert;

    LLSD& _initLLSD();
    LLSD mLLSDInfo;
};


// class LLBasicCertificateVector
// Class representing a list of certificates
// This implementation uses a stl vector of certificates.
class LLBasicCertificateVector : virtual public LLCertificateVector
{

public:
    LLBasicCertificateVector() {}

    virtual ~LLBasicCertificateVector() {}

    // Implementation of the basic iterator implementation.
    // The implementation uses a vector iterator derived from
    // the vector in the LLBasicCertificateVector class
    class BasicIteratorImpl : public iterator_impl
    {
    public:
        BasicIteratorImpl(std::vector<LLPointer<LLCertificate> >::iterator _iter) { mIter = _iter;}
        virtual ~BasicIteratorImpl() {};
        // seek forward or back.  Used by the operator++/operator-- implementations
        virtual void seek(bool incr)
        {
            if(incr)
            {
                mIter++;
            }
            else
            {
                mIter--;
            }
        }
        // create a copy of the iterator implementation class, used by the iterator copy constructor
        virtual LLPointer<iterator_impl> clone() const
        {
            return new BasicIteratorImpl(mIter);
        }

        virtual bool equals(const LLPointer<iterator_impl>& _iter) const
        {
            const BasicIteratorImpl *rhs_iter = dynamic_cast<const BasicIteratorImpl *>(_iter.get());
            llassert(rhs_iter);
            if (!rhs_iter) return 0;
            return (mIter == rhs_iter->mIter);
        }
        virtual LLPointer<LLCertificate> get()
        {
            return *mIter;
        }
    protected:
        friend class LLBasicCertificateVector;
        std::vector<LLPointer<LLCertificate> >::iterator mIter;
    };

    // numeric index of the vector
    virtual LLPointer<LLCertificate> operator[](int _index) { return mCerts[_index];}

    // Iteration
    virtual iterator begin() { return iterator(new BasicIteratorImpl(mCerts.begin())); }

    virtual iterator end() {  return iterator(new BasicIteratorImpl(mCerts.end())); }

    // find a cert given params
    virtual iterator find(const LLSD& params);

    // return the number of certs in the store
    virtual int size() const { return static_cast<int>(mCerts.size()); }

    // insert the cert to the store.  if a copy of the cert already exists in the store, it is removed first
    virtual void  add(LLPointer<LLCertificate> cert) { insert(end(), cert); }

    // insert the cert to the store.  if a copy of the cert already exists in the store, it is removed first
    virtual void  insert(iterator _iter, LLPointer<LLCertificate> cert);

    // remove a certificate from the store
    virtual LLPointer<LLCertificate> erase(iterator _iter);

protected:
    std::vector<LLPointer<LLCertificate> >mCerts;
};

// class LLCertificateStore
// represents a store of certificates, typically a store of root CA
// certificates.  The store can be persisted, and can be used to validate
// a cert chain
//
class LLBasicCertificateStore : virtual public LLBasicCertificateVector, public LLCertificateStore
{
public:
    LLBasicCertificateStore(const std::string& filename);
    void load_from_file(const std::string& filename);

    virtual ~LLBasicCertificateStore();

    // persist the store
    virtual void save();

    // return the store id
    virtual std::string storeId() const;

    // validate a certificate chain against a certificate store, using the
    // given validation policy.
    virtual void validate(int validation_policy,
                          LLPointer<LLCertificateChain> ca_chain,
                          const LLSD& validation_params);

    // Clears cache of certs validated agains store
    virtual void clearSertCache() { mTrustedCertCache.clear(); }

protected:
    std::vector<LLPointer<LLCertificate> >            mCerts;

    // cache of cert sha1 hashes to from/to date pairs, to improve
    // performance of cert trust.  Note, these are not the CA certs,
    // but the certs that have been validated against this store.
    typedef std::map<std::string, std::pair<LLDate, LLDate> > t_cert_cache;
    t_cert_cache mTrustedCertCache;

    std::string mFilename;
};

// class LLCertificateChain
// Class representing a chain of certificates in order, with the
// first element being the child cert.
class LLBasicCertificateChain : virtual public LLBasicCertificateVector, public LLCertificateChain
{

public:
    LLBasicCertificateChain(X509_STORE_CTX * store);

    virtual ~LLBasicCertificateChain() {}

};



// LLSecAPIBasicCredential class
class LLSecAPIBasicCredential : public LLCredential
{
public:
    LLSecAPIBasicCredential(const std::string& grid) : LLCredential(grid) {}
    virtual ~LLSecAPIBasicCredential() {}
    // return a value representing the user id, used for server and voice
    // (could be guid, name in format "name_resident", whatever)
    virtual std::string userID() const;

    // printible string identifying the credential.
    virtual std::string asString() const;
};

// LLSecAPIBasicHandler Class
// Interface handler class for the various security storage handlers.
class LLSecAPIBasicHandler : public LLSecAPIHandler
{
public:

    LLSecAPIBasicHandler(const std::string& protected_data_filename,
                         const std::string& legacy_password_path);
    LLSecAPIBasicHandler();

    void init();

    virtual ~LLSecAPIBasicHandler();

    // instantiate a certificate from a pem string
    virtual LLPointer<LLCertificate> getCertificate(const std::string& pem_cert);


    // instiate a certificate from an openssl X509 structure
    virtual LLPointer<LLCertificate> getCertificate(X509* openssl_cert);

    // instantiate a chain from an X509_STORE_CTX
    virtual LLPointer<LLCertificateChain> getCertificateChain(X509_STORE_CTX* chain);

    // instantiate a cert store given it's id.  if a persisted version
    // exists, it'll be loaded.  If not, one will be created (but not
    // persisted)
    virtual LLPointer<LLCertificateStore> getCertificateStore(const std::string& store_id);

    // protectedData functions technically should be pretected or private,
    // they are not because of llsechandler_basic_test imlementation

    // persist data in a protected store
    virtual void setProtectedData(const std::string& data_type,
                                  const std::string& data_id,
                                  const LLSD& data);

    // retrieve protected data
    virtual LLSD getProtectedData(const std::string& data_type,
                                  const std::string& data_id);

    // delete a protected data item from the store
    virtual void deleteProtectedData(const std::string& data_type,
                                     const std::string& data_id);

    // persist data in a protected store's map
    virtual void addToProtectedMap(const std::string& data_type,
                                   const std::string& data_id,
                                   const std::string& map_elem,
                                   const LLSD& data);

    // remove data from protected store's map
    virtual void removeFromProtectedMap(const std::string& data_type,
                                        const std::string& data_id,
                                        const std::string& map_elem);

    // ensure protected store's map is written to storage
    virtual void syncProtectedMap();

    // credential management routines

    virtual LLPointer<LLCredential> createCredential(const std::string& grid,
                                                     const LLSD& identifier,
                                                     const LLSD& authenticator);

    // load single credencial from default storage
    virtual LLPointer<LLCredential> loadCredential(const std::string& grid);

    // save credencial to default storage
    virtual void saveCredential(LLPointer<LLCredential> cred, bool save_authenticator);

    virtual void deleteCredential(LLPointer<LLCredential> cred);

    // has map of credentials declared as specific storage
    virtual bool hasCredentialMap(const std::string& storage,
                                  const std::string& grid);

    // returns true if map is empty or does not exist
    virtual bool emptyCredentialMap(const std::string& storage,
                                    const std::string& grid);

    // load map of credentials from specific storage
    virtual void loadCredentialMap(const std::string& storage,
                                   const std::string& grid,
                                   credential_map_t& credential_map);

    // load single username from map of credentials from specific storage
    virtual LLPointer<LLCredential> loadFromCredentialMap(const std::string& storage,
                                                          const std::string& grid,
                                                          const std::string& userid);

    // add item to map of credentials from specific storage
    virtual void addToCredentialMap(const std::string& storage,
                                    LLPointer<LLCredential> cred,
                                    bool save_authenticator);

    // remove item from map of credentials from specific storage
    virtual void removeFromCredentialMap(const std::string& storage,
                                         LLPointer<LLCredential> cred);

    // remove item from map of credentials from specific storage
    virtual void removeFromCredentialMap(const std::string& storage,
                                         const std::string& grid,
                                         const std::string& userid);

    virtual void removeCredentialMap(const std::string& storage,
                                     const std::string& grid);


protected:
    void _readProtectedData(unsigned char *unique_id, U32 id_len);
    void _readProtectedData();
    void _writeProtectedData();
    std::string _legacyLoadPassword();

    std::string mProtectedDataFilename;
    LLSD mProtectedDataMap;
    LLPointer<LLBasicCertificateStore> mStore;

    std::string mLegacyPasswordPath;
};

bool valueCompareLLSD(const LLSD& lhs, const LLSD& rhs);

#ifdef LL_WINDOWS
#pragma warning (pop)
#endif // LL_WINDOWS

#endif // LLSECHANDLER_BASIC