account.cpp
/* vi: set et sw=4 ts=4 cino=t0,(0: */
/*
* This file is part of libaccounts-qt
*
* Copyright (C) 2009-2011 Nokia Corporation.
* Copyright (C) 2012-2016 Canonical Ltd.
*
* Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation.
*
* 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 St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include "account.h"
#include "manager.h"
#include "manager_p.h"
#include "utils.h"
#include <QPointer>
#include <libaccounts-glib/ag-account.h>
namespace Accounts {
class Account::Private
{
public:
Private(Manager *manager, const QString &providerName, Account *account);
Private(Manager *manager, AgAccount *agAccount);
~Private()
{
g_cancellable_cancel(m_cancellable);
g_object_unref(m_cancellable);
m_cancellable = NULL;
}
void init(Account *account);
QPointer<Manager> m_manager;
AgAccount *m_account; //real account
GCancellable *m_cancellable;
QString prefix;
static void on_display_name_changed(Account *self);
static void on_enabled(Account *self, const gchar *service_name,
gboolean enabled);
static void account_store_cb(AgAccount *account,
GAsyncResult *res,
Account *self);
static void on_deleted(Account *self);
};
class Watch::Private
{
public:
static void account_notify_cb(AgAccount *account, const gchar *key,
Watch *self);
};
} //namespace Accounts
using namespace Accounts;
static QChar slash = QChar::fromLatin1('/');
Watch::Watch(QObject *parent):
QObject(parent)
{
}
Watch::~Watch()
{
Account *account = qobject_cast<Account *>(QObject::parent());
/* The destructor of Account deletes the child Watches before detaching
* them, so here account should always be not NULL */
Q_ASSERT(account != NULL);
ag_account_remove_watch(account->d->m_account, watch);
}
Account::Private::Private(Manager *manager, const QString &providerName,
Account *account):
m_manager(manager),
m_cancellable(g_cancellable_new())
{
m_account = ag_manager_create_account(manager->d->m_manager,
providerName.toUtf8().constData());
init(account);
}
Account::Private::Private(Manager *manager, AgAccount *agAccount):
m_manager(manager),
m_account(agAccount),
m_cancellable(g_cancellable_new())
{
}
void Account::Private::init(Account *account)
{
if (m_account == nullptr) return;
g_signal_connect_swapped(m_account, "display-name-changed",
G_CALLBACK(&Private::on_display_name_changed),
account);
g_signal_connect_swapped(m_account, "enabled",
G_CALLBACK(&Private::on_enabled), account);
g_signal_connect_swapped(m_account, "deleted",
G_CALLBACK(&Private::on_deleted), account);
}
void Account::Private::on_display_name_changed(Account *self)
{
const gchar *name = ag_account_get_display_name(self->d->m_account);
Q_EMIT self->displayNameChanged(UTF8(name));
}
void Account::Private::on_enabled(Account *self, const gchar *service_name,
gboolean enabled)
{
Q_EMIT self->enabledChanged(UTF8(service_name), enabled);
}
void Account::Private::on_deleted(Account *self)
{
Q_EMIT self->removed();
}
Account::Account(Manager *manager, const QString &providerName,
QObject *parent):
QObject(parent),
d(new Private(manager, providerName, this))
{
}
Account::Account(Private *d, QObject *parent):
QObject(parent),
d(d)
{
d->init(this);
}
Account *Account::fromId(Manager *manager, AccountId id, QObject *parent)
{
GError *error = nullptr;
AgAccount *account = ag_manager_load_account(manager->d->m_manager, id,
&error);
if (account == nullptr) {
Q_ASSERT(error != nullptr);
manager->d->lastError = Error(error);
g_error_free(error);
return 0;
}
Q_ASSERT(error == nullptr);
return new Account(new Private(manager, account), parent);
}
Account::~Account()
{
QObjectList list = children();
for (int i = 0; i < list.count(); i++)
{
QObject *o = list.at(i);
if (qobject_cast<Watch *>(o))
delete o;
}
g_signal_handlers_disconnect_by_func
(d->m_account, (void *)&Private::on_display_name_changed, this);
g_signal_handlers_disconnect_by_func
(d->m_account, (void *)&Private::on_enabled, this);
g_signal_handlers_disconnect_by_func
(d->m_account, (void *)&Private::on_deleted, this);
g_object_unref(d->m_account);
delete d;
d = nullptr;
}
AccountId Account::id() const
{
return d->m_account ? d->m_account->id : 0;
}
Manager *Account::manager() const
{
return d->m_manager;
}
bool Account::supportsService(const QString &serviceType) const
{
return ag_account_supports_service(d->m_account,
serviceType.toUtf8().constData());
}
ServiceList Account::services(const QString &serviceType) const
{
GList *list;
if (serviceType.isEmpty()) {
list = ag_account_list_services(d->m_account);
} else {
list = ag_account_list_services_by_type(d->m_account,
serviceType.toUtf8().constData());
}
/* convert glist -> ServiceList */
ServiceList servList;
GList *iter;
for (iter = list; iter; iter = iter->next)
{
AgService *service = (AgService*)iter->data;
servList.append(Service(service, StealReference));
}
g_list_free(list);
return servList;
}
ServiceList Account::enabledServices() const
{
GList *list;
list = ag_account_list_enabled_services(d->m_account);
/* convert glist -> ServiceList */
ServiceList servList;
GList *iter;
for (iter = list; iter; iter = g_list_next(iter))
{
AgService *service = (AgService*)iter->data;
servList.append(Service(service, StealReference));
}
g_list_free(list);
return servList;
}
bool Account::enabled() const
{
return isEnabled();
}
bool Account::isEnabled() const
{
return ag_account_get_enabled(d->m_account);
}
void Account::setEnabled(bool enabled)
{
ag_account_set_enabled(d->m_account, enabled);
}
QString Account::displayName() const
{
return UTF8(ag_account_get_display_name(d->m_account));
}
void Account::setDisplayName(const QString &displayName)
{
ag_account_set_display_name(d->m_account,
displayName.toUtf8().constData());
}
QString Account::providerName() const
{
return UTF8(ag_account_get_provider_name(d->m_account));
}
Provider Account::provider() const
{
return manager()->provider(providerName());
}
void Account::selectService(const Service &service)
{
AgService *agService = NULL;
if (service.isValid())
agService = service.service();
ag_account_select_service(d->m_account, agService);
d->prefix = QString();
}
Service Account::selectedService() const
{
AgService *agService = ag_account_get_selected_service(d->m_account);
return Service(agService);
}
QStringList Account::allKeys() const
{
QStringList allKeys;
AgAccountSettingIter iter;
const gchar *key;
GVariant *val;
/* iterate the settings */
QByteArray tmp = d->prefix.toLatin1();
ag_account_settings_iter_init(d->m_account, &iter, tmp.constData());
while (ag_account_settings_iter_get_next(&iter, &key, &val))
{
allKeys.append(QString(ASCII(key)));
}
return allKeys;
}
void Account::beginGroup(const QString &prefix)
{
d->prefix += prefix + slash;
}
QStringList Account::childGroups() const
{
QStringList groups, all_keys;
all_keys = allKeys();
Q_FOREACH (const QString &key, all_keys)
{
if (key.contains(slash)) {
QString group = key.section(slash, 0, 0);
if (!groups.contains(group))
groups.append(group);
}
}
return groups;
}
QStringList Account::childKeys() const
{
QStringList keys, all_keys;
all_keys = allKeys();
Q_FOREACH (const QString &key, all_keys)
{
if (!key.contains(slash))
keys.append(key);
}
return keys;
}
void Account::clear()
{
/* clear() must ignore the group: so, temporarily reset it and call
* remove("") */
QString saved_prefix = d->prefix;
d->prefix = QString();
remove(QString());
d->prefix = saved_prefix;
}
bool Account::contains(const QString &key) const
{
return childKeys().contains(key);
}
void Account::endGroup()
{
d->prefix = d->prefix.section(slash, 0, -3,
QString::SectionIncludeTrailingSep);
if (d->prefix[0] == slash) d->prefix.remove(0, 1);
}
QString Account::group() const
{
if (d->prefix.endsWith(slash))
return d->prefix.left(d->prefix.size() - 1);
return d->prefix;
}
bool Account::isWritable() const
{
return true;
}
void Account::remove(const QString &key)
{
if (key.isEmpty())
{
/* delete all keys in the group */
QStringList keys = allKeys();
Q_FOREACH (const QString &key, keys)
{
if (!key.isEmpty())
remove(key);
}
}
else
{
QString full_key = d->prefix + key;
QByteArray tmpkey = full_key.toLatin1();
ag_account_set_variant(d->m_account, tmpkey.constData(), NULL);
}
}
void Account::setValue(const QString &key, const QVariant &value)
{
GVariant *variant = qVariantToGVariant(value);
if (variant == nullptr) {
return;
}
QString full_key = d->prefix + key;
QByteArray tmpkey = full_key.toLatin1();
ag_account_set_variant(d->m_account, tmpkey.constData(), variant);
}
void Account::Private::account_store_cb(AgAccount *account,
GAsyncResult *res,
Account *self)
{
GError *error = NULL;
ag_account_store_finish(account, res, &error);
if (error) {
if (error->domain == G_IO_ERROR &&
error->code == G_IO_ERROR_CANCELLED) {
} else {
Q_EMIT self->error(Error(error));
}
g_error_free(error);
} else {
Q_EMIT self->synced();
}
}
QVariant Account::value(const QString &key, const QVariant &defaultValue,
SettingSource *source) const
{
QString full_key = d->prefix + key;
QByteArray ba = full_key.toLatin1();
AgSettingSource settingSource;
GVariant *variant =
ag_account_get_variant(d->m_account, ba.constData(), &settingSource);
if (source != nullptr) {
switch (settingSource) {
case AG_SETTING_SOURCE_ACCOUNT: *source = ACCOUNT; break;
case AG_SETTING_SOURCE_PROFILE: *source = TEMPLATE; break;
default: *source = NONE; break;
}
}
return (variant != nullptr) ? gVariantToQVariant(variant) : defaultValue;
}
SettingSource Account::value(const QString &key, QVariant &value) const
{
SettingSource source;
QVariant variant = this->value(key, QVariant(), &source);
if (variant.isValid()) {
if (value.type() != variant.type()) {
if (!variant.convert(value.type())) source = NONE;
}
value = variant;
}
return source;
}
QString Account::valueAsString(const QString &key,
QString default_value,
SettingSource *source) const
{
QVariant var = default_value;
SettingSource src = value(key, var);
if (source)
*source = src;
return var.toString();
}
int Account::valueAsInt(const QString &key,
int default_value,
SettingSource *source) const
{
QVariant var = default_value;
SettingSource src = value(key, var);
if (source)
*source = src;
return var.toInt();
}
quint64 Account::valueAsUInt64(const QString &key,
quint64 default_value,
SettingSource *source) const
{
QVariant var = default_value;
SettingSource src = value(key, var);
if (source)
*source = src;
return var.toULongLong();
}
bool Account::valueAsBool(const QString &key,
bool default_value,
SettingSource *source) const
{
QVariant var = default_value;
SettingSource src = value(key, var);
if (source)
*source = src;
return var.toBool();
}
void Watch::Private::account_notify_cb(AgAccount *account, const gchar *key,
Watch *watch)
{
Q_EMIT watch->notify(key);
Q_UNUSED(account);
}
Watch *Account::watchKey(const QString &key)
{
AgAccountWatch ag_watch;
Watch *watch = new Watch(this);
if (!key.isEmpty())
{
QString full_key = d->prefix + key;
ag_watch = ag_account_watch_key
(d->m_account, full_key.toLatin1().constData(),
(AgAccountNotifyCb)&Watch::Private::account_notify_cb, watch);
}
else
{
ag_watch = ag_account_watch_dir
(d->m_account, d->prefix.toLatin1().constData(),
(AgAccountNotifyCb)&Watch::Private::account_notify_cb, watch);
}
if (!ag_watch)
{
delete watch;
return NULL;
}
watch->setWatch(ag_watch);
return watch;
}
void Account::sync()
{
ag_account_store_async(d->m_account,
d->m_cancellable,
(GAsyncReadyCallback)&Private::account_store_cb,
this);
}
bool Account::syncAndBlock()
{
GError *error = NULL;
bool ret;
ret = ag_account_store_blocking(d->m_account, &error);
if (error)
{
qWarning() << "Store operation failed: " << error->message;
g_error_free(error);
}
return ret;
}
void Account::remove()
{
ag_account_delete(d->m_account);
}
void Account::sign(const QString &key, const char *token)
{
ag_account_sign (d->m_account, key.toUtf8().constData(), token);
}
bool Account::verify(const QString &key, const char **token)
{
return ag_account_verify(d->m_account, key.toUtf8().constData(), token);
}
bool Account::verifyWithTokens(const QString &key, QList<const char*> tokens)
{
int tokensCount = tokens.count();
const char *tmp[tokensCount + 1];
for (int i = 0; i < tokensCount; ++i)
{
tmp[i] = tokens.at(i);
}
tmp[tokensCount] = NULL;
return ag_account_verify_with_tokens(d->m_account, key.toUtf8().constData(), tmp);
}
uint Account::credentialsId()
{
QString key = ACCOUNTS_KEY_CREDENTIALS_ID;
QVariant val(QVariant::Int);
if (value(key, val) != NONE)
return val.toUInt();
uint id = 0;
Service service = selectedService();
if (service.isValid()) {
selectService();
if (value(key, val) != NONE)
id = val.toUInt();
selectService(service);
}
return id;
}
AgAccount *Account::account()
{
return d->m_account;
}
Accounts::Manager::lastError
Error lastError() const
Gets the last error.
Definition: manager.cpp:583
Accounts::Manager
Manager of accounts, services and providers.
Definition: manager.h:52
Accounts::AccountService::account
Account * account() const
Return the Account.
Definition: account-service.cpp:205
Accounts::Error
Base object definition for accounts error handling.
Definition: error.h:44
Accounts::Manager::provider
Provider provider(const QString &providerName) const
Gets an object representing a provider.
Definition: manager.cpp:421
Accounts::Service
Representation of an account service.
Definition: service.h:49
Accounts::Provider
Representation of an account provider.
Definition: provider.h:49
Accounts::Service::isValid
bool isValid() const
Check whether this object represents a Service.
Definition: service.cpp:104