Files
wxWidgets/src/unix/secretstore.cpp
Vadim Zeitlin a03441f959 Suppress harmless -Wmissing-fields-initialize in wxSecretStore
This warning is difficult to avoid as we don't want to initialize the
unused/reserved fields of SecretSchema struct, yet the compiler warns
about it (when using -Wextra).
2017-11-12 16:57:40 +01:00

306 lines
9.8 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Name: src/unix/secretstore.cpp
// Purpose: wxSecretStore implementation using libsecret.
// Author: Vadim Zeitlin
// Created: 2016-05-27
// Copyright: (c) 2016 Vadim Zeitlin <vadim@wxwidgets.org>
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
// for compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#if wxUSE_SECRETSTORE
#include "wx/secretstore.h"
#include "wx/private/secretstore.h"
// Older versions of libsecret, such as 0.16 from Ubuntu 14.04 LTS, require
// predefining this symbol in order to have access to the APIs we use below.
#define SECRET_API_SUBJECT_TO_CHANGE
#include <libsecret/secret.h>
#include "wx/gtk/private/error.h"
#include "wx/gtk/private/list.h"
#include "wx/gtk/private/object.h"
namespace
{
// Tiny helper to ensure freeing GHashTable.
class wxGHashTable
{
public:
// Ctor takes ownership of its argument.
explicit wxGHashTable(GHashTable* hash) : m_hash(hash) { }
// Must have a copy ctor to allow returning these objects from functions in
// C++98, this class could/should be move-only in C++11.
wxGHashTable(const wxGHashTable& other)
: m_hash(other.m_hash)
{
g_hash_table_ref(m_hash);
}
~wxGHashTable() { g_hash_table_unref(m_hash); }
operator GHashTable *() const { return m_hash; }
private:
GHashTable* const m_hash;
wxDECLARE_NO_ASSIGN_CLASS(wxGHashTable);
};
// ============================================================================
// wxSecretStoreImpl using libsecret
// ============================================================================
class wxSecretValueLibSecretImpl : public wxSecretValueImpl
{
public:
// Create a new secret value.
//
// Notice that we have to use text/plain as content type and not
// application/octet-stream which would have been more logical because
// libsecret accepts only valid UTF-8 strings for the latter, while our
// data is not necessarily UTF-8 (nor even text at all...).
wxSecretValueLibSecretImpl(size_t size, const void* data)
: m_value(secret_value_new(static_cast<const gchar*>(data), size,
"text/plain"))
{
}
// Adopt an existing secret value.
//
// This ctor takes ownership of the provided pointer and will release it
// when this object is destroyed.
explicit wxSecretValueLibSecretImpl(SecretValue* value)
: m_value(value)
{
}
virtual ~wxSecretValueLibSecretImpl()
{
// No need to wipe memory, this will happen by default.
secret_value_unref(m_value);
}
virtual size_t GetSize() const wxOVERRIDE
{
gsize length = 0;
(void)secret_value_get(m_value, &length);
return length;
}
virtual const void *GetData() const wxOVERRIDE
{
return secret_value_get(m_value, NULL);
}
SecretValue* GetValue() const
{
return m_value;
}
private:
SecretValue* const m_value;
};
// This implementation uses synchronous libsecret functions which is supposed
// to be a bad idea, but doesn't seem to be a big deal in practice and as there
// is no simple way to implement asynchronous API under the other platforms, it
// doesn't seem to be worth it to use it just under Unix, so keep things simple
// (even if blocking) for now.
class wxSecretStoreLibSecretImpl : public wxSecretStoreImpl
{
public:
virtual bool Save(const wxString& service,
const wxString& user,
const wxSecretValueImpl& secret,
wxString& errmsg) wxOVERRIDE
{
// We don't have any argument for the user-visible secret description
// supported by libsecret, so we just reuse the service string. It
// might be a good idea to add a possibility to specify a more
// informative description later.
// Notice that we can't use secret_password_store_sync() here because
// our secret can contain NULs, so we must pass by the lower level API.
wxGtkError error;
if ( !secret_service_store_sync
(
NULL, // Default service
GetSchema(),
BuildAttributes(service, user),
SECRET_COLLECTION_DEFAULT,
service.utf8_str(),
static_cast<const wxSecretValueLibSecretImpl&>(secret).GetValue(),
NULL, // Can't be cancelled
error.Out()
) )
{
errmsg = error.GetMessage();
return false;
}
return true;
}
virtual bool Load(const wxString& service,
wxString* user,
wxSecretValueImpl** secret,
wxString& errmsg) const wxOVERRIDE
{
wxGtkError error;
GList* const found = secret_service_search_sync
(
NULL, // Default service
GetSchema(),
BuildAttributes(service),
static_cast<SecretSearchFlags>
(
SECRET_SEARCH_UNLOCK |
SECRET_SEARCH_LOAD_SECRETS
),
NULL, // Can't be cancelled
error.Out()
);
if ( !found )
{
// There can be no error message if the secret was just not found
// and no other error occurred -- just leave the error message
// empty in this case, this is exactly how our API is supposed to
// behave.
if ( error )
errmsg = error.GetMessage();
return false;
}
wxGtkList ensureListFreed(found);
SecretItem* const item = static_cast<SecretItem*>(found->data);
wxGtkObject<SecretItem> ensureItemFreed(item);
const wxGHashTable attrs(secret_item_get_attributes(item));
const gpointer field = g_hash_table_lookup(attrs, FIELD_USER);
if ( field )
*user = wxString::FromUTF8(static_cast<char*>(field));
*secret = new wxSecretValueLibSecretImpl(secret_item_get_secret(item));
return true;
}
virtual bool Delete(const wxString& service,
wxString& errmsg) wxOVERRIDE
{
wxGtkError error;
if ( !secret_password_clearv_sync
(
GetSchema(),
BuildAttributes(service),
NULL, // Can't be cancelled
error.Out()
) )
{
if ( error )
errmsg = error.GetMessage();
return false;
}
return true;
}
private:
// Constants for the schema fields.
static const char* FIELD_SERVICE;
static const char* FIELD_USER;
// Currently we use a hard-coded schema, but we might allow customizing it
// (or at least its name?) in the future, so wrap access to it in this
// helper function to make changing the code later simpler.
static SecretSchema* GetSchema()
{
// SecretSchema struct has some "reserved" fields in it which we don't
// want to initialize, but this results in this warning if it's
// enabled, so just suppress it here.
wxGCC_WARNING_SUPPRESS(missing-field-initializers)
static SecretSchema s_schema =
{
"org.freedesktop.Secret.Generic",
SECRET_SCHEMA_NONE,
{
{ FIELD_SERVICE, SECRET_SCHEMA_ATTRIBUTE_STRING },
{ FIELD_USER, SECRET_SCHEMA_ATTRIBUTE_STRING },
{ NULL }
}
};
wxGCC_WARNING_RESTORE(missing-field-initializers)
return &s_schema;
}
// Return attributes for the schema defined above.
static wxGHashTable BuildAttributes(const wxString& service)
{
return wxGHashTable(secret_attributes_build
(
GetSchema(),
FIELD_SERVICE, service.utf8_str().data(),
NULL
));
}
static wxGHashTable BuildAttributes(const wxString& service,
const wxString& user)
{
return wxGHashTable(secret_attributes_build
(
GetSchema(),
FIELD_SERVICE, service.utf8_str().data(),
FIELD_USER, user.utf8_str().data(),
NULL
));
}
};
const char* wxSecretStoreLibSecretImpl::FIELD_SERVICE = "service";
const char* wxSecretStoreLibSecretImpl::FIELD_USER = "user";
} // anonymous namespace
// ============================================================================
// LibSecret-specific implementation of common methods
// ============================================================================
/* static */
wxSecretValueImpl* wxSecretValue::NewImpl(size_t size, const void *data)
{
return new wxSecretValueLibSecretImpl(size, data);
}
/* static */
wxSecretStore wxSecretStore::GetDefault()
{
// There is only a single store under Windows anyhow.
return wxSecretStore(new wxSecretStoreLibSecretImpl());
}
#endif // wxUSE_SECRETSTORE