Improve the credentialsId() method
[accounts-sso:accounts-qt.git] / Accounts / account.cpp
1 /* vi: set et sw=4 ts=4 cino=t0,(0: */
2 /*
3  * This file is part of libaccounts-qt
4  *
5  * Copyright (C) 2009-2010 Nokia Corporation.
6  *
7  * Contact: Alberto Mardegan <alberto.mardegan@nokia.com>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * version 2.1 as published by the Free Software Foundation.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  */
23
24 #include "account.h"
25 #include "manager.h"
26
27 #include <libaccounts-glib/ag-manager.h>
28 #include <libaccounts-glib/ag-account.h>
29
30 namespace Accounts {
31
32 class Account::Private
33 {
34 public:
35     Private()
36     {
37         m_account = 0;
38     }
39
40     ~Private() {}
41
42     AgAccount *m_account;  //real account
43     QString prefix;
44
45     static void on_display_name_changed(Account *self);
46     static void on_enabled(Account *self, const gchar *service_name,
47                            gboolean enabled);
48     static void account_store_cb(AgAccount *account, const GError *error,
49                                  Account *self);
50     static void on_deleted(Account *self);
51 };
52
53 class Watch::Private
54 {
55 public:
56     static void account_notify_cb(AgAccount *account, const gchar *key,
57                                   Watch *self);
58 };
59 } //namespace Accounts
60
61
62 using namespace Accounts;
63
64 static QChar slash = QChar::fromLatin1('/');
65
66 Watch::Watch(QObject *parent)
67     : QObject(parent)
68 {
69 }
70
71 Watch::~Watch()
72 {
73     TRACE();
74     Account *account = qobject_cast<Account *>(QObject::parent());
75     /* The destructor of Account deletes the child Watches before detaching
76      * them, so here account should always be not NULL */
77     Q_ASSERT(account != NULL);
78     ag_account_remove_watch(account->d->m_account, watch);
79 }
80
81 void Account::Private::on_display_name_changed(Account *self)
82 {
83     TRACE();
84     const gchar *name = ag_account_get_display_name(self->d->m_account);
85
86     emit self->displayNameChanged(UTF8(name));
87 }
88
89 void Account::Private::on_enabled(Account *self, const gchar *service_name,
90                                   gboolean enabled)
91 {
92     TRACE();
93
94     emit self->enabledChanged(UTF8(service_name), enabled);
95 }
96
97 void Account::Private::on_deleted(Account *self)
98 {
99     TRACE();
100
101     emit self->removed();
102 }
103
104 Account::Account(AgAccount *account, QObject *parent)
105     : QObject(parent), d(new Private)
106 {
107     TRACE();
108     d->m_account = account;
109     g_object_ref(account);
110
111     g_signal_connect_swapped(account, "display-name-changed",
112                              G_CALLBACK(&Private::on_display_name_changed),
113                              this);
114     g_signal_connect_swapped(account, "enabled",
115                              G_CALLBACK(&Private::on_enabled), this);
116     g_signal_connect_swapped(account, "deleted",
117                              G_CALLBACK(&Private::on_deleted), this);
118 }
119
120 Account::~Account()
121 {
122     TRACE();
123
124     QObjectList list = children();
125     for (int i = 0; i < list.count(); i++)
126     {
127         QObject *o = list.at(i);
128         if (qobject_cast<Watch *>(o))
129             delete o;
130     }
131
132     g_signal_handlers_disconnect_by_func
133         (d->m_account, (void *)&Private::on_display_name_changed, this);
134     g_signal_handlers_disconnect_by_func
135         (d->m_account, (void *)&Private::on_enabled, this);
136     g_signal_handlers_disconnect_by_func
137         (d->m_account, (void *)&Private::on_deleted, this);
138     g_object_unref(d->m_account);
139     delete d;
140     d = 0;
141 }
142
143 AccountId Account::id() const
144 {
145     return d->m_account ? d->m_account->id : 0;
146 }
147
148 Manager *Account::manager() const
149 {
150     return qobject_cast<Manager *>(QObject::parent());
151 }
152
153 bool Account::supportsService(const QString &serviceType) const
154 {
155     TRACE() << serviceType;
156
157     return ag_account_supports_service(d->m_account,
158                                        serviceType.toUtf8().constData());
159 }
160
161 ServiceList Account::services(const QString &serviceType) const
162 {
163     TRACE() << serviceType;
164
165     GList *list;
166     if (serviceType.isEmpty()) {
167         list = ag_account_list_services(d->m_account);
168     } else {
169         list = ag_account_list_services_by_type(d->m_account,
170             serviceType.toUtf8().constData());
171     }
172
173     /* convert glist -> ServiceList */
174     ServiceList servList;
175     GList *iter;
176     Manager *mgr = manager();
177     Q_ASSERT(mgr != 0);
178     for (iter = list; iter; iter = g_list_next(iter))
179     {
180         Service *serv = mgr->serviceInstance((AgService*)(iter->data));
181         servList.append(serv);
182     }
183
184     ag_service_list_free(list);
185
186     return servList;
187 }
188
189 bool Account::enabled() const
190 {
191     return ag_account_get_enabled(d->m_account);
192 }
193
194 void Account::setEnabled(bool enabled)
195 {
196     ag_account_set_enabled(d->m_account, enabled);
197 }
198
199 QString Account::displayName() const
200 {
201     return UTF8(ag_account_get_display_name(d->m_account));
202 }
203
204 void Account::setDisplayName(const QString &displayName)
205 {
206     ag_account_set_display_name(d->m_account,
207                                 displayName.toUtf8().constData());
208 }
209
210 QString Account::providerName() const
211 {
212     return UTF8(ag_account_get_provider_name(d->m_account));
213 }
214
215 void Account::selectService(const Service *service)
216 {
217     AgService *agService = NULL;
218
219     if (service != NULL)
220         agService = service->service();
221
222     ag_account_select_service(d->m_account, agService);
223     d->prefix = QString();
224 }
225
226 Service* Account::selectedService() const
227 {
228     AgService *agService = ag_account_get_selected_service(d->m_account);
229     if (agService == NULL)
230         return NULL;
231
232     Manager *mgr = manager();
233     Q_ASSERT(mgr != 0);
234     Service *service = mgr->serviceInstance(agService);
235
236     return service;
237 }
238
239 QStringList Account::allKeys() const
240 {
241     QStringList allKeys;
242     AgAccountSettingIter iter;
243     const gchar *key;
244     const GValue *val;
245
246     /* iterate the settings */
247     QByteArray tmp = d->prefix.toLatin1();
248     ag_account_settings_iter_init(d->m_account, &iter, tmp.constData());
249     while (ag_account_settings_iter_next(&iter, &key, &val))
250     {
251         allKeys.append(QString(ASCII(key)).mid(d->prefix.size()));
252     }
253     return allKeys;
254 }
255
256 void Account::beginGroup(const QString &prefix)
257 {
258     TRACE();
259     d->prefix += prefix + slash;
260 }
261
262 QStringList Account::childGroups() const
263 {
264     QStringList groups, all_keys;
265
266     all_keys = allKeys();
267     foreach (QString key, all_keys)
268     {
269         if (key.contains(slash)) {
270             QString group = key.section(slash, 0, 0);
271             if (!groups.contains(group))
272                 groups.append(group);
273         }
274     }
275     return groups;
276 }
277
278 QStringList Account::childKeys() const
279 {
280     QStringList keys, all_keys;
281
282     all_keys = allKeys();
283     foreach (QString key, all_keys)
284     {
285         if (!key.contains(slash))
286             keys.append(key);
287     }
288     return keys;
289 }
290
291 void Account::clear()
292 {
293     /* clear() must ignore the group: so, temporarily reset it and call
294      * remove("") */
295     QString saved_prefix = d->prefix;
296     d->prefix = QString();
297     remove(QString());
298     d->prefix = saved_prefix;
299 }
300
301 bool Account::contains(const QString &key) const
302 {
303     return childKeys().contains(key);
304 }
305
306 void Account::endGroup()
307 {
308     d->prefix = d->prefix.section(slash, 0, -3,
309                                   QString::SectionIncludeTrailingSep);
310     if (d->prefix[0] == slash) d->prefix.remove(0, 1);
311 }
312
313 QString Account::group() const
314 {
315     if (d->prefix.endsWith(slash))
316         return d->prefix.left(d->prefix.size() - 1);
317     return d->prefix;
318 }
319
320 bool Account::isWritable() const
321 {
322     return true;
323 }
324
325 void Account::remove(const QString &key)
326 {
327     if (key.isEmpty())
328     {
329         /* delete all keys in the group */
330         QStringList keys = allKeys();
331         foreach (QString key, keys)
332         {
333             if (!key.isEmpty())
334                 remove(key);
335         }
336     }
337     else
338     {
339         QString full_key = d->prefix + key;
340         QByteArray tmpkey = full_key.toLatin1();
341         ag_account_set_value(d->m_account, tmpkey.constData(), NULL);
342     }
343 }
344
345 void Account::setValue(const QString &key, const QVariant &value)
346 {
347     TRACE();
348     GValue val= {0, {{0}}};
349     QByteArray tmpvalue;
350
351     switch (value.type())
352     {
353     case QVariant::String:
354         g_value_init(&val, G_TYPE_STRING);
355         tmpvalue = value.toString().toUtf8();
356         g_value_set_static_string(&val, tmpvalue.constData());
357         break;
358     case QVariant::Int:
359         g_value_init(&val, G_TYPE_INT);
360         g_value_set_int(&val, value.toInt());
361         break;
362     case QVariant::UInt:
363         g_value_init(&val, G_TYPE_UINT);
364         g_value_set_uint(&val, value.toUInt());
365         break;
366     case QVariant::LongLong:
367         g_value_init(&val, G_TYPE_INT64);
368         g_value_set_int64(&val, value.toLongLong());
369         break;
370     case QVariant::ULongLong:
371         g_value_init(&val, G_TYPE_UINT64);
372         g_value_set_uint64(&val, value.toULongLong());
373         break;
374     case QVariant::Bool:
375         g_value_init(&val, G_TYPE_BOOLEAN);
376         g_value_set_boolean(&val, value.toBool());
377         break;
378     default:
379         qWarning("unsupproted datatype %s", value.typeName());
380         return;
381     }
382
383     QString full_key = d->prefix + key;
384     QByteArray tmpkey = full_key.toLatin1();
385     ag_account_set_value(d->m_account, tmpkey.constData(), &val);
386     g_value_unset(&val);
387 }
388
389 void Account::Private::account_store_cb(AgAccount *account, const GError *err,
390                                         Account *self)
391 {
392     TRACE() << "Saved accunt ID:" << account->id;
393
394     if (err) {
395         emit self->error((ErrorCode)err->code);
396     } else {
397         emit self->synced();
398     }
399
400     Q_UNUSED(account);
401 }
402
403 void Account::sync()
404 {
405     TRACE();
406
407     ag_account_store(d->m_account,
408                      (AgAccountStoreCb)&Private::account_store_cb,
409                      this);
410 }
411
412 bool Account::syncAndBlock()
413 {
414     TRACE();
415
416     GError *error = NULL;
417     bool ret;
418
419     ret = ag_account_store_blocking(d->m_account, &error);
420     if (error)
421     {
422         qWarning() << "Store operation failed: " << error->message;
423         g_error_free(error);
424     }
425
426     return ret;
427 }
428
429 SettingSource Account::value(const QString &key, QVariant &value) const
430 {
431     GType type;
432
433     switch (value.type())
434     {
435     case QVariant::String:
436         type = G_TYPE_STRING;
437         break;
438     case QVariant::Int:
439         type = G_TYPE_INT;
440         break;
441     case QVariant::UInt:
442         type = G_TYPE_UINT;
443         break;
444     case QVariant::LongLong:
445         type = G_TYPE_INT64;
446         break;
447     case QVariant::ULongLong:
448         type = G_TYPE_UINT64;
449         break;
450     case QVariant::Bool:
451         type = G_TYPE_BOOLEAN;
452         break;
453     default:
454         qWarning("Unsupported type %s", value.typeName());
455         return NONE;
456     }
457
458     GValue val= {0, {{0}}};
459     g_value_init(&val, type);
460     QString full_key = d->prefix + key;
461     AgSettingSource source =
462         ag_account_get_value(d->m_account,
463                              full_key.toLatin1().constData(), &val);
464     TRACE() << "Source:" << source;
465     if (source == AG_SETTING_SOURCE_NONE)
466         return NONE;
467
468     switch (type)
469     {
470     case G_TYPE_STRING:
471         value = UTF8(g_value_get_string(&val));
472         break;
473     case G_TYPE_INT:
474         value = g_value_get_int(&val);
475         break;
476     case G_TYPE_UINT:
477         value = g_value_get_uint(&val);
478         break;
479     case G_TYPE_INT64:
480         value = g_value_get_int64(&val);
481         break;
482     case G_TYPE_UINT64:
483         value = g_value_get_uint64(&val);
484         break;
485     case G_TYPE_BOOLEAN:
486         value = g_value_get_boolean(&val);
487         break;
488     default:
489         /* This can never be reached */
490         Q_ASSERT(false);
491         break;
492     }
493     g_value_unset(&val);
494
495     return (source == AG_SETTING_SOURCE_ACCOUNT) ? ACCOUNT : TEMPLATE;
496 }
497
498 QString Account::valueAsString(const QString &key,
499                                QString default_value,
500                                SettingSource *source) const
501 {
502     QVariant var = default_value;
503     SettingSource src = value(key, var);
504     if (source)
505         *source = src;
506     return var.toString();
507 }
508
509 int Account::valueAsInt(const QString &key,
510                         int default_value,
511                         SettingSource *source) const
512 {
513     QVariant var = default_value;
514     SettingSource src = value(key, var);
515     if (source)
516         *source = src;
517     return var.toInt();
518 }
519
520 quint64 Account::valueAsUInt64(const QString &key,
521                         quint64 default_value,
522                         SettingSource *source) const
523 {
524     QVariant var = default_value;
525     SettingSource src = value(key, var);
526     if (source)
527         *source = src;
528     return var.toULongLong();
529 }
530
531 bool Account::valueAsBool(const QString &key,
532                           bool default_value,
533                           SettingSource *source) const
534 {
535     QVariant var = default_value;
536     SettingSource src = value(key, var);
537     if (source)
538         *source = src;
539     return var.toBool();
540 }
541
542 void Watch::Private::account_notify_cb(AgAccount *account, const gchar *key,
543                                        Watch *watch)
544 {
545     emit watch->notify(key);
546
547     Q_UNUSED(account);
548 }
549
550 Watch *Account::watchKey(const QString &key)
551 {
552     AgAccountWatch ag_watch;
553     Watch *watch = new Watch(this);
554
555     if (!key.isEmpty())
556     {
557         QString full_key = d->prefix + key;
558         ag_watch = ag_account_watch_key
559             (d->m_account, full_key.toLatin1().constData(),
560              (AgAccountNotifyCb)&Watch::Private::account_notify_cb, watch);
561     }
562     else
563     {
564         ag_watch = ag_account_watch_dir
565             (d->m_account, d->prefix.toLatin1().constData(),
566              (AgAccountNotifyCb)&Watch::Private::account_notify_cb, watch);
567     }
568
569     if (!ag_watch)
570     {
571         delete watch;
572         return NULL;
573     }
574
575     watch->setWatch(ag_watch);
576     return watch;
577 }
578
579 void Account::remove()
580 {
581     TRACE();
582     ag_account_delete(d->m_account);
583 }
584
585 void Account::sign(const QString &key, const char *token)
586 {
587     ag_account_sign (d->m_account, key.toUtf8().constData(), token);
588 }
589
590 bool Account::verify(const QString &key, const char **token)
591 {
592     return ag_account_verify(d->m_account, key.toUtf8().constData(), token);
593 }
594
595 bool Account::verifyWithTokens(const QString &key, QList<const char*> tokens)
596 {
597     int tokensCount = tokens.count();
598
599     const char *tmp[tokensCount + 1];
600
601     for (int i = 0; i < tokensCount; ++i)
602     {
603         tmp[i] = tokens.at(i);
604     }
605     tmp[tokensCount] = NULL;
606
607     return ag_account_verify_with_tokens(d->m_account, key.toUtf8().constData(), tmp);
608 }
609
610 qint32 Account::credentialsId()
611 {
612     QString key = ACCOUNTS_KEY_CREDENTIALS_ID;
613     QVariant val(QVariant::Int);
614
615     if (value(key, val) != NONE)
616         return val.toInt();
617
618     qint32 id = 0;
619     Service *service = selectedService();
620     if (service) {
621         selectService(NULL);
622         if (value(key, val) != NONE)
623             id = val.toInt();
624         selectService(service);
625     }
626     return id;
627 }