///////////////////////////////////////////////////////////////////////////////
// Name:        wx/secretstore.h
// Purpose:     Storing and retrieving secrets using OS-provided facilities.
// Author:      Vadim Zeitlin
// Created:     2016-05-27
// Copyright:   (c) 2016 Vadim Zeitlin <vadim@wxwidgets.org>
// Licence:     wxWindows licence
///////////////////////////////////////////////////////////////////////////////

#ifndef _WX_SECRETSTORE_H_
#define _WX_SECRETSTORE_H_

#include "wx/defs.h"

#include "wx/string.h"

#if wxUSE_SECRETSTORE

// Initial version of wxSecretStore required passing user name to Load(), which
// didn't make much sense without support for multiple usernames per service,
// so the API was changed to load the username too. Test for this symbol to
// distinguish between the old and the new API, it wasn't defined before the
// API change.
#define wxHAS_SECRETSTORE_LOAD_USERNAME

class wxSecretStoreImpl;
class wxSecretValueImpl;

// ----------------------------------------------------------------------------
// Represents a secret value, e.g. a password string.
// ----------------------------------------------------------------------------

// This is an immutable value-like class which tries to ensure that the secret
// value will be wiped out from memory once it's not needed any more.
class WXDLLIMPEXP_BASE wxSecretValue
{
public:
    // Creates an empty secret value (not the same as an empty password).
    wxSecretValue() : m_impl(nullptr) { }

    // Creates a secret value from the given data.
    wxSecretValue(size_t size, const void *data)
        : m_impl(NewImpl(size, data, "application/octet-stream"))
    {
    }

    // Creates a secret value from string.
    explicit wxSecretValue(const wxString& secret)
    {
        const wxScopedCharBuffer buf(secret.utf8_str());
        m_impl = NewImpl(buf.length(), buf.data(), "text/plain");
    }

    wxSecretValue(const wxSecretValue& other);
    wxSecretValue& operator=(const wxSecretValue& other);

    ~wxSecretValue();

    // Check if a secret is not empty.
    bool IsOk() const { return m_impl != nullptr; }

    // Compare with another secret.
    bool operator==(const wxSecretValue& other) const;
    bool operator!=(const wxSecretValue& other) const
    {
        return !(*this == other);
    }

    // Get the size, in bytes, of the secret data.
    size_t GetSize() const;

    // Get read-only access to the secret data.
    //
    // Don't assume it is NUL-terminated, use GetSize() instead.
    const void *GetData() const;

    // Get the secret data as a string.
    //
    // Notice that you may want to overwrite the string contents after using it
    // by calling WipeString().
    wxString GetAsString(const wxMBConv& conv = wxConvWhateverWorks) const;

    // Erase the given area of memory overwriting its presumably sensitive
    // content.
    static void Wipe(size_t size, void *data);

    // Overwrite the contents of the given wxString.
    static void WipeString(wxString& str);

private:
    // This method is implemented in platform-specific code and must return a
    // new heap-allocated object initialized with the given data.
    static wxSecretValueImpl*
    NewImpl(size_t size, const void *data, const char* contentType);

    // This ctor is only used by wxSecretStore and takes ownership of the
    // provided existing impl pointer.
    explicit wxSecretValue(wxSecretValueImpl* impl) : m_impl(impl) { }

    wxSecretValueImpl* m_impl;

    friend class wxSecretStore;
};

// ----------------------------------------------------------------------------
// A collection of secrets, sometimes called a key chain.
// ----------------------------------------------------------------------------

class WXDLLIMPEXP_BASE wxSecretStore
{
public:
    // Returns the default secrets collection to use.
    //
    // Currently this is the only way to create a secret store object. In the
    // future we could add more factory functions to e.g. create non-persistent
    // stores or allow creating stores corresponding to the native facilities
    // being used (e.g. specify schema name under Linux or a SecKeychainRef
    // under OS X).
    static wxSecretStore GetDefault();

    // This class has no default ctor, use GetDefault() instead.

    // But it can be copied, a copy refers to the same store as the original.
    wxSecretStore(const wxSecretStore& store);

    // Dtor is not virtual, this class is not supposed to be derived from.
    ~wxSecretStore();


    // Check if this object is valid, i.e. can be used, and optionally fill in
    // the provided error message string if it isn't.
    bool IsOk(wxString* errmsg = nullptr) const;


    // Store a username/password combination.
    //
    // The service name should be user readable and unique.
    //
    // If a secret with the same service name already exists, it will be
    // overwritten with the new value.
    //
    // Returns false after logging an error message if an error occurs,
    // otherwise returns true indicating that the secret has been stored.
    bool Save(const wxString& service,
              const wxString& username,
              const wxSecretValue& password);

    // Look up the username/password for the given service.
    //
    // If no username/password is found for the given service, false is
    // returned.
    //
    // Otherwise the function returns true and updates the provided user name
    // and password arguments.
    bool Load(const wxString& service,
              wxString& username,
              wxSecretValue& password) const;

    // Delete a previously stored username/password combination.
    //
    // If anything was deleted, returns true. Otherwise returns false and
    // logs an error if any error other than not finding any matches occurred.
    bool Delete(const wxString& service);

private:
    // Ctor takes ownership of the passed pointer.
    explicit wxSecretStore(wxSecretStoreImpl* impl) : m_impl(impl) { }

    wxSecretStoreImpl* const m_impl;
};

#else // !wxUSE_SECRETSTORE

#include "wx/utils.h"

// Provide stand in for wxSecretValue allowing to use it without having #if
// wxUSE_SECRETSTORE checks everywhere. Unlike the real version, this class
// doesn't provide any added security.
class wxSecretValue
{
public:
    wxSecretValue() { m_valid = false; }

    wxSecretValue(size_t size, const void *data)
    {
        Init(size, data);
    }

    explicit wxSecretValue(const wxString& secret)
    {
        const wxScopedCharBuffer buf(secret.utf8_str());
        Init(buf.length(), buf.data());
    }

    bool IsOk() const { return m_valid; }

    bool operator==(const wxSecretValue& other) const
    {
        return m_valid == other.m_valid && m_data == other.m_data;
    }

    bool operator!=(const wxSecretValue& other) const
    {
        return !(*this == other);
    }

    size_t GetSize() const { return m_data.utf8_str().length(); }

    const void *GetData() const { return m_data.utf8_str().data(); }

    wxString GetAsString(const wxMBConv& conv = wxConvWhateverWorks) const
    {
        wxUnusedVar(conv);
        return m_data;
    }

    static void Wipe(size_t size, void *data) { wxSecureZeroMemory(data, size); }
    static void WipeString(wxString& str)
    {
        str.assign(str.length(), '*');
        str.clear();
    }

private:
    void Init(size_t size, const void *data)
    {
        m_data = wxString::From8BitData(static_cast<const char*>(data), size);
        m_valid = true;
    }

    wxString m_data;
    bool m_valid;
};

#endif // wxUSE_SECRETSTORE/!wxUSE_SECRETSTORE

// Helper class ensuring WipeString() is called.
//
// It should only be used as a local variable and never polymorphically.
class wxSecretString : public wxString
{
public:
    wxSecretString()
    {
    }

    wxSecretString(const wxString& value)
        : wxString(value)
    {
    }

    explicit wxSecretString(const wxSecretValue& value)
        : wxString(value.GetAsString())
    {
    }

    ~wxSecretString()
    {
        wxSecretValue::WipeString(*this);
    }
};

#endif // _WX_SECRETSTORE_H_
