Fix bug 271519 - Ovi Authentication plugin is getting restarted unnecessarily.. which...
[accounts-sso:jlaako-signon.git] / src / signond / signonsessioncore.cpp
1 /*
2  * This file is part of signon
3  *
4  * Copyright (C) 2009-2010 Nokia Corporation.
5  *
6  * Contact: Alberto Mardegan <alberto.mardegan@nokia.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * version 2.1 as published by the Free Software Foundation.
11  *
12  * This library is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20  * 02110-1301 USA
21  */
22
23 #include "signond-common.h"
24 #include "signonauthsession.h"
25 #include "signonidentityinfo.h"
26 #include "signonidentity.h"
27 #include "signonauthsessionadaptor.h"
28 #include "signonui_interface.h"
29 #include "accesscontrolmanager.h"
30
31 #include "SignOn/uisessiondata_priv.h"
32 #include "SignOn/authpluginif.h"
33 #include "SignOn/signonerror.h"
34
35 #define MAX_IDLE_TIME SIGNOND_MAX_IDLE_TIME
36 /*
37  * the watchdog searches for idle sessions with period of half of idle timeout
38  * */
39 #define IDLE_WATCHDOG_TIMEOUT SIGNOND_MAX_IDLE_TIME * 500
40
41 #define SSO_KEY_USERNAME QLatin1String("UserName")
42 #define SSO_KEY_PASSWORD QLatin1String("Secret")
43 #define SSO_KEY_CAPTION QLatin1String("Caption")
44 #define SSO_KEY_KEEPALIVE QLatin1String("KeepAlive")
45
46 using namespace SignonDaemonNS;
47 using namespace SignOnCrypto;
48
49 /*
50  * cache of session queues, as was mentined they cannot be static
51  * */
52 QMap<QString, SignonSessionCore *> sessionsOfStoredCredentials;
53 /*
54  * List of "zero" authsessions, needed for global signout
55  * */
56 QList<SignonSessionCore *> sessionsOfNonStoredCredentials;
57
58 static QVariantMap filterVariantMap(const QVariantMap &other)
59 {
60     QVariantMap result;
61
62     foreach(QString key, other.keys()) {
63         if (!other.value(key).isNull() && other.value(key).isValid())
64             result.insert(key, other.value(key));
65     }
66
67     return result;
68 }
69
70 static QString sessionName(const quint32 id, const QString &method)
71 {
72    return QString::number(id) + QLatin1String("+") + method;
73 }
74
75 static pid_t pidOfContext(const QDBusConnection &connection, const QDBusMessage &message)
76 {
77     return connection.interface()->servicePid(message.service()).value();
78 }
79
80 SignonSessionCore::SignonSessionCore(quint32 id,
81                                      const QString &method,
82                                      int timeout,
83                                      SignonDaemon *parent)
84     : SignonDisposable(timeout, parent),
85       m_id(id),
86       m_method(method),
87       m_passwordUpdate(QString()),
88       m_queryCredsUiDisplayed(false),
89       m_refCount(0)
90 {
91     m_signonui = NULL;
92     m_watcher = NULL;
93
94     if (!(m_encryptor = new Encryptor))
95         qFatal("Cannot allocate memory for encryptor");
96
97     m_signonui = new SignonUiAdaptor(
98                                     SIGNON_UI_SERVICE,
99                                     SIGNON_UI_DAEMON_OBJECTPATH,
100                                     QDBusConnection::sessionBus());
101
102     connect(CredentialsAccessManager::instance(),
103             SIGNAL(credentialsSystemReady()),
104             SLOT(credentialsSystemReady()));
105
106     setAutoDestruct(false);
107
108 }
109
110 SignonSessionCore::~SignonSessionCore()
111 {
112     AuthCoreCache::instance()->authSessionDestroyed(
113         AuthCoreCache::CacheId(m_id, m_method));
114
115     delete m_plugin;
116     delete m_watcher;
117     delete m_signonui;
118     delete m_encryptor;
119
120     m_plugin = NULL;
121     m_signonui = NULL;
122     m_watcher = NULL;
123 }
124
125 SignonSessionCore *SignonSessionCore::sessionCore(const quint32 id, const QString &method, SignonDaemon *parent)
126 {
127     QString objectName;
128     QString key = sessionName(id, method);
129
130     if (id) {
131         if (sessionsOfStoredCredentials.contains(key)) {
132             return sessionsOfStoredCredentials.value(key);
133         }
134     }
135
136     SignonSessionCore *ssc = new SignonSessionCore(id, method, parent->authSessionTimeout(), parent);
137
138     if (ssc->setupPlugin() == false) {
139         TRACE() << "The resulted object is corrupted and has to be deleted";
140         delete ssc;
141         return NULL;
142     }
143
144     if (id)
145         sessionsOfStoredCredentials.insert(key, ssc);
146     else
147         sessionsOfNonStoredCredentials.append(ssc);
148
149     TRACE() << "The new session is created :" << key;
150     return ssc;
151 }
152
153 quint32 SignonSessionCore::id() const
154 {
155     TRACE();
156     keepInUse();
157     return m_id;
158 }
159
160 QString SignonSessionCore::method() const
161 {
162     TRACE();
163     keepInUse();
164     return m_method;
165 }
166
167 bool SignonSessionCore::setupPlugin()
168 {
169     m_plugin = PluginProxy::createNewPluginProxy(m_method);
170
171     if (!m_plugin) {
172         TRACE() << "Plugin of type " << m_method << " cannot be found";
173         return false;
174     }
175
176     connect(m_plugin,
177             SIGNAL(processResultReply(const QString&, const QVariantMap&)),
178             this,
179             SLOT(processResultReply(const QString&, const QVariantMap&)),
180             Qt::DirectConnection);
181
182     connect(m_plugin,
183             SIGNAL(processStore(const QString&, const QVariantMap&)),
184             this,
185             SLOT(processStore(const QString&, const QVariantMap&)),
186             Qt::DirectConnection);
187
188     connect(m_plugin,
189             SIGNAL(processUiRequest(const QString&, const QVariantMap&)),
190             this,
191             SLOT(processUiRequest(const QString&, const QVariantMap&)),
192             Qt::DirectConnection);
193
194     connect(m_plugin,
195             SIGNAL(processRefreshRequest(const QString&, const QVariantMap&)),
196             this,
197             SLOT(processRefreshRequest(const QString&, const QVariantMap&)),
198             Qt::DirectConnection);
199
200     connect(m_plugin,
201             SIGNAL(processError(const QString&, int, const QString&)),
202             this,
203             SLOT(processError(const QString&, int, const QString&)),
204             Qt::DirectConnection);
205
206     connect(m_plugin,
207             SIGNAL(stateChanged(const QString&, int, const QString&)),
208             this,
209             SLOT(stateChangedSlot(const QString&, int, const QString&)),
210             Qt::DirectConnection);
211
212     return true;
213 }
214
215 void SignonSessionCore::stopAllAuthSessions()
216 {
217     qDeleteAll(sessionsOfStoredCredentials);
218     sessionsOfStoredCredentials.clear();
219
220     qDeleteAll(sessionsOfNonStoredCredentials);
221     sessionsOfNonStoredCredentials.clear();
222 }
223
224 QStringList SignonSessionCore::loadedPluginMethods(const QString &method)
225 {
226     foreach (SignonSessionCore *corePtr, sessionsOfStoredCredentials) {
227         if (corePtr->method() == method)
228             return corePtr->queryAvailableMechanisms(QStringList());
229     }
230
231     foreach (SignonSessionCore *corePtr, sessionsOfNonStoredCredentials) {
232         if (corePtr->method() == method)
233             return corePtr->queryAvailableMechanisms(QStringList());
234     }
235
236     return QStringList();
237 }
238
239 QStringList SignonSessionCore::queryAvailableMechanisms(const QStringList &wantedMechanisms)
240 {
241     keepInUse();
242
243     if (!wantedMechanisms.size())
244         return m_plugin->mechanisms();
245
246     return m_plugin->mechanisms().toSet().intersect(wantedMechanisms.toSet()).toList();
247 }
248
249 void SignonSessionCore::process(const QDBusConnection &connection,
250                                  const QDBusMessage &message,
251                                  const QVariantMap &sessionDataVa,
252                                  const QString &mechanism,
253                                  const QString &cancelKey)
254 {
255     keepInUse();
256     if (m_encryptor->isVariantMapEncrypted(sessionDataVa)) {
257         pid_t pid = pidOfContext(connection, message);
258         QVariantMap decodedData(m_encryptor->
259                                 decodeVariantMap(sessionDataVa, pid));
260         if (m_encryptor->status() != Encryptor::Ok) {
261             replyError(connection,
262                        message,
263                        Error::EncryptionFailure,
264                        QString::fromLatin1("Failed to decrypt incoming message"));
265             return;
266         }
267         m_listOfRequests.enqueue(RequestData(connection,
268                                              message,
269                                              decodedData,
270                                              mechanism,
271                                              cancelKey));
272     } else {
273         m_listOfRequests.enqueue(RequestData(connection,
274                                              message,
275                                              sessionDataVa,
276                                              mechanism,
277                                              cancelKey));
278     }
279
280     if (CredentialsAccessManager::instance()->isCredentialsSystemReady())
281         QMetaObject::invokeMethod(this, "startNewRequest", Qt::QueuedConnection);
282 }
283
284 void SignonSessionCore::cancel(const QString &cancelKey)
285 {
286     TRACE();
287
288     int requestIndex;
289     for (requestIndex = 0; requestIndex < m_listOfRequests.size(); requestIndex++) {
290         if (m_listOfRequests.at(requestIndex).m_cancelKey == cancelKey)
291             break;
292     }
293
294     TRACE() << "The request is found with index " << requestIndex;
295
296     if (requestIndex < m_listOfRequests.size()) {
297         if (requestIndex == 0) {
298             m_canceled = cancelKey;
299             m_plugin->cancel();
300
301             if (m_watcher && !m_watcher->isFinished()) {
302                 m_signonui->cancelUiRequest(cancelKey);
303                 delete m_watcher;
304                 m_watcher = 0;
305             }
306         }
307
308         /*
309          * We must let to the m_listOfRequests to have the canceled request data
310          * in order to delay the next request execution until the actual cancelation
311          * will happen. We will know about that precisely: plugin must reply via
312          * resultSlot or via errorSlot.
313          * */
314         RequestData rd((requestIndex == 0 ?
315                         m_listOfRequests.head() :
316                         m_listOfRequests.takeAt(requestIndex)));
317
318         QDBusMessage errReply = rd.m_msg.createErrorReply(SIGNOND_SESSION_CANCELED_ERR_NAME,
319                                                           SIGNOND_SESSION_CANCELED_ERR_STR);
320         rd.m_conn.send(errReply);
321         TRACE() << "Size of the queue is " << m_listOfRequests.size();
322     }
323 }
324
325 void SignonSessionCore::setId(quint32 id)
326 {
327     keepInUse();
328
329     if (m_id == id)
330         return;
331
332     QString key;
333
334     if (id == 0) {
335         key = sessionName(m_id, m_method);
336         sessionsOfNonStoredCredentials.append(sessionsOfStoredCredentials.take(key));
337     } else {
338         key = sessionName(id, m_method);
339         if (sessionsOfStoredCredentials.contains(key)) {
340             qCritical() << "attempt to assign existing id";
341             return;
342         }
343
344         sessionsOfNonStoredCredentials.removeOne(this);
345         sessionsOfStoredCredentials[key] = this;
346     }
347     m_id = id;
348 }
349
350 void SignonSessionCore::startProcess()
351 {
352
353     TRACE() << "the number of requests is : " << m_listOfRequests.length();
354
355     keepInUse();
356
357     RequestData data = m_listOfRequests.head();
358     QVariantMap parameters = data.m_params;
359
360     if (m_id) {
361         CredentialsDB *db = CredentialsAccessManager::instance()->credentialsDB();
362         Q_ASSERT(db != 0);
363
364         SignonIdentityInfo info = db->credentials(m_id);
365         if (info.id() != SIGNOND_NEW_IDENTITY) {
366             if (!parameters.contains(SSO_KEY_PASSWORD)) {
367                 //If secrets db not available attempt loading data from cache
368                 if (db->isSecretsDBOpen()) {
369                     parameters[SSO_KEY_PASSWORD] = info.password();
370                 }
371
372                 /* Temporary fix - keep it until session core refactoring is complete and auth cache
373                  * will be dumped in the secrets db. */
374                 if (parameters[SSO_KEY_PASSWORD].toString().isEmpty()) {
375                     AuthCache *cache = AuthCoreCache::instance()->data(info.id());
376                     if (cache != 0) {
377                         TRACE() << "Using cached secret.";
378                         parameters[SSO_KEY_PASSWORD] = cache->password();
379                     } else {
380                         TRACE() << "Secrets storage not available and authentication "
381                                    "cache is empty - if SSO requires a password, "
382                                    "auth. will fail.";
383                     }
384                 }
385             }
386             //database overrules over sessiondata for validated username,
387             //so that identity cannot be misused
388             if (info.validated() || !parameters.contains(SSO_KEY_USERNAME)) {
389                 parameters[SSO_KEY_USERNAME] = info.userName();
390             }
391
392             pid_t pid = pidOfContext(data.m_conn, data.m_msg);
393             QSet<QString> clientTokenSet = AccessControlManager::accessTokens(pid).toSet();
394             QSet<QString> identityAclTokenSet = info.accessControlList().toSet();
395             QSet<QString> paramsTokenSet = clientTokenSet.intersect(identityAclTokenSet);
396
397             if (!paramsTokenSet.isEmpty()) {
398                 QStringList tokenList = paramsTokenSet.toList();
399                 parameters[SSO_ACCESS_CONTROL_TOKENS] = tokenList;
400             }
401         } else {
402             BLAME() << "Error occurred while getting data from credentials database.";
403         }
404
405         QVariantMap storedParams;
406         if (db->isSecretsDBOpen()) {
407             storedParams = db->loadData(m_id, m_method);
408         }
409         /* Temporary fix - keep it until session core refactoring is complete and auth cache
410          * will be dumped in the secrets db. */
411         if (storedParams.isEmpty()) {
412             AuthCache *cache = AuthCoreCache::instance()->data(info.id());
413             if (cache != 0) {
414                 TRACE() << "Using cached BLOB data.";
415                 storedParams = cache->blobData();
416             }
417         }
418
419         //parameters will overwrite any common keys on stored params
420         parameters = mergeVariantMaps(storedParams, parameters);
421     }
422
423     if (parameters.contains(SSOUI_KEY_UIPOLICY)
424         && parameters[SSOUI_KEY_UIPOLICY] == RequestPasswordPolicy) {
425         parameters.remove(SSO_KEY_PASSWORD);
426     }
427
428     /* Temporary caching, if credentials are valid
429      * this data will be effectively cached */
430     m_tmpUsername = parameters[SSO_KEY_USERNAME].toString();
431     m_tmpPassword = parameters[SSO_KEY_PASSWORD].toString();
432
433     if (parameters.contains(SSO_KEY_KEEPALIVE)) {
434         setAutoDestruct(!parameters[SSO_KEY_KEEPALIVE].toBool());
435     }
436
437     if (!m_plugin->process(data.m_cancelKey, parameters, data.m_mechanism)) {
438         QDBusMessage errReply = data.m_msg.createErrorReply(SIGNOND_RUNTIME_ERR_NAME,
439                                                             SIGNOND_RUNTIME_ERR_STR);
440         data.m_conn.send(errReply);
441         m_listOfRequests.removeFirst();
442         QMetaObject::invokeMethod(this, "startNewRequest", Qt::QueuedConnection);
443     } else
444         stateChangedSlot(data.m_cancelKey, SignOn::SessionStarted, QLatin1String("The request is started successfully"));
445 }
446
447 void SignonSessionCore::replyError(const QDBusConnection &conn, const QDBusMessage &msg, int err, const QString &message)
448 {
449     keepInUse();
450
451     QString errName;
452     QString errMessage;
453
454     //TODO this is needed for old error codes
455     if(err < Error::AuthSessionErr) {
456         BLAME() << "Deprecated error code: " << err;
457             if (message.isEmpty())
458                 errMessage = SIGNOND_UNKNOWN_ERR_STR;
459             else
460                 errMessage = message;
461             errName = SIGNOND_UNKNOWN_ERR_NAME;
462     }
463
464     if (Error::AuthSessionErr < err && err < Error::UserErr) {
465         switch(err) {
466             case Error::MechanismNotAvailable:
467                 errName = SIGNOND_MECHANISM_NOT_AVAILABLE_ERR_NAME;
468                 errMessage = SIGNOND_MECHANISM_NOT_AVAILABLE_ERR_STR;
469                 break;
470             case Error::MissingData:
471                 errName = SIGNOND_MISSING_DATA_ERR_NAME;
472                 errMessage = SIGNOND_MISSING_DATA_ERR_STR;
473                 break;
474             case Error::InvalidCredentials:
475                 errName = SIGNOND_INVALID_CREDENTIALS_ERR_NAME;
476                 errMessage = SIGNOND_INVALID_CREDENTIALS_ERR_STR;
477                 break;
478             case Error::NotAuthorized:
479                 errName = SIGNOND_NOT_AUTHORIZED_ERR_NAME;
480                 errMessage = SIGNOND_NOT_AUTHORIZED_ERR_STR;
481                 break;
482             case Error::WrongState:
483                 errName = SIGNOND_WRONG_STATE_ERR_NAME;
484                 errMessage = SIGNOND_WRONG_STATE_ERR_STR;
485                 break;
486             case Error::OperationNotSupported:
487                 errName = SIGNOND_OPERATION_NOT_SUPPORTED_ERR_NAME;
488                 errMessage = SIGNOND_OPERATION_NOT_SUPPORTED_ERR_STR;
489                 break;
490             case Error::NoConnection:
491                 errName = SIGNOND_NO_CONNECTION_ERR_NAME;
492                 errMessage = SIGNOND_NO_CONNECTION_ERR_STR;
493                 break;
494             case Error::Network:
495                 errName = SIGNOND_NETWORK_ERR_NAME;
496                 errMessage = SIGNOND_NETWORK_ERR_STR;
497                 break;
498             case Error::Ssl:
499                 errName = SIGNOND_SSL_ERR_NAME;
500                 errMessage = SIGNOND_SSL_ERR_STR;
501                 break;
502             case Error::Runtime:
503                 errName = SIGNOND_RUNTIME_ERR_NAME;
504                 errMessage = SIGNOND_RUNTIME_ERR_STR;
505                 break;
506             case Error::SessionCanceled:
507                 errName = SIGNOND_SESSION_CANCELED_ERR_NAME;
508                 errMessage = SIGNOND_SESSION_CANCELED_ERR_STR;
509                 break;
510             case Error::TimedOut:
511                 errName = SIGNOND_TIMED_OUT_ERR_NAME;
512                 errMessage = SIGNOND_TIMED_OUT_ERR_STR;
513                 break;
514             case Error::UserInteraction:
515                 errName = SIGNOND_USER_INTERACTION_ERR_NAME;
516                 errMessage = SIGNOND_USER_INTERACTION_ERR_STR;
517                 break;
518             case Error::OperationFailed:
519                 errName = SIGNOND_OPERATION_FAILED_ERR_NAME;
520                 errMessage = SIGNOND_OPERATION_FAILED_ERR_STR;
521                 break;
522             case Error::EncryptionFailure:
523                 errName = SIGNOND_ENCRYPTION_FAILED_ERR_NAME;
524                 errMessage = SIGNOND_ENCRYPTION_FAILED_ERR_STR;
525                 break;
526             case Error::TOSNotAccepted:
527                 errName = SIGNOND_TOS_NOT_ACCEPTED_ERR_NAME;
528                 errMessage = SIGNOND_TOS_NOT_ACCEPTED_ERR_STR;
529                 break;
530             case Error::ForgotPassword:
531                 errName = SIGNOND_FORGOT_PASSWORD_ERR_NAME;
532                 errMessage = SIGNOND_FORGOT_PASSWORD_ERR_STR;
533                 break;
534             case Error::IncorrectDate:
535                 errName = SIGNOND_INCORRECT_DATE_ERR_NAME;
536                 errMessage = SIGNOND_INCORRECT_DATE_ERR_STR;
537                 break;
538             default:
539                 if (message.isEmpty())
540                     errMessage = SIGNOND_UNKNOWN_ERR_STR;
541                 else
542                     errMessage = message;
543                 errName = SIGNOND_UNKNOWN_ERR_NAME;
544                 break;
545         };
546     }
547
548     if(err > Error::UserErr) {
549         errName = SIGNOND_USER_ERROR_ERR_NAME;
550         errMessage = (QString::fromLatin1("%1:%2")).arg(err).arg(message);
551     }
552
553     QDBusMessage errReply;
554     errReply = msg.createErrorReply(errName, ( message.isEmpty() ? errMessage : message ));
555     conn.send(errReply);
556 }
557
558 void SignonSessionCore::processStoreOperation(const StoreOperation &operation)
559 {
560     TRACE() << "Processing store operation.";
561     CredentialsDB *db = CredentialsAccessManager::instance()->credentialsDB();
562     Q_ASSERT(db != 0);
563
564     if (operation.m_storeType != StoreOperation::Blob) {
565
566         SignonIdentityInfo info = operation.m_info;
567
568         QVariantMap data = operation.m_credsData;
569
570         //allow update only for not validated username
571         if (!info.validated()
572                 && data.contains(SSO_KEY_USERNAME)
573                 && !data[SSO_KEY_USERNAME].toString().isEmpty())
574             info.setUserName(data[SSO_KEY_USERNAME].toString());
575
576         if (!m_passwordUpdate.isEmpty())
577             info.setPassword(m_passwordUpdate);
578
579         if (data.contains(SSO_KEY_PASSWORD)
580             && !data[SSO_KEY_PASSWORD].toString().isEmpty())
581             info.setPassword(data[SSO_KEY_PASSWORD].toString());
582
583         info.setValidated(true);
584
585         if (!(db->updateCredentials(info))) {
586             BLAME() << "Error occured while updating credentials.";
587         }
588
589     } else {
590         TRACE() << "Processing --- StoreOperation::Blob";
591
592         if(!db->storeData(m_id,
593                           operation.m_authMethod,
594                           operation.m_blobData)) {
595             BLAME() << "Error occured while storing data.";
596         }
597     }
598 }
599
600 void SignonSessionCore::processResultReply(const QString &cancelKey, const QVariantMap &data)
601 {
602     TRACE();
603
604     keepInUse();
605
606     if (!m_listOfRequests.size())
607         return;
608
609     RequestData rd = m_listOfRequests.dequeue();
610
611     if (cancelKey != m_canceled) {
612         QVariantList arguments;
613         QVariantMap filteredData = filterVariantMap(data);
614
615         CredentialsAccessManager *camManager = CredentialsAccessManager::instance();
616         CredentialsDB *db = camManager->credentialsDB();
617         Q_ASSERT(db != 0);
618
619         //put temporary password from ui interaction into result if plugin didn't return new password
620         if (!filteredData.contains(SSO_KEY_PASSWORD) && !m_tmpPassword.isEmpty()) {
621             filteredData[SSO_KEY_PASSWORD] = m_tmpPassword;
622             m_tmpPassword.clear();
623         }
624
625         //update database entry
626         if (m_id != SIGNOND_NEW_IDENTITY) {
627             SignonIdentityInfo info = db->credentials(m_id);
628
629             StoreOperation storeOp(StoreOperation::Credentials);
630             storeOp.m_credsData = filteredData;
631             storeOp.m_info = info;
632
633             /* If the credentials are validated, the secrets db is not available and
634              * not authorized keys are available inform the CAM about the situation. */
635             if (info.validated() && !db->isSecretsDBOpen()) {
636                 /* Send the storage not available event only if the curent result
637                  * processing is following a previous signon UI query. This is
638                  * to avoid unexpected UI pop-ups.
639                  */
640                 if (m_queryCredsUiDisplayed) {
641                     m_storeQueue.enqueue(storeOp);
642
643                     SecureStorageEvent *event =
644                         new SecureStorageEvent(
645                             (QEvent::Type)SIGNON_SECURE_STORAGE_NOT_AVAILABLE);
646
647                     event->m_sender = static_cast<QObject *>(this);
648
649                     QCoreApplication::postEvent(
650                         CredentialsAccessManager::instance(),
651                         event,
652                         Qt::HighEventPriority);
653                 }
654             } else {
655                 processStoreOperation(storeOp);
656             }
657         }
658
659         /* If secrets db not available cache credentials for this session core.
660          * Avoid creating an invalid caching record - cache only if the password
661          * is not empty. */
662         if (!db->isSecretsDBOpen() && !m_tmpPassword.isEmpty()) {
663             AuthCache *cache = new AuthCache;
664             cache->setUsername(m_tmpUsername);
665             cache->setPassword(m_tmpPassword);
666             m_tmpUsername.clear();
667             m_tmpPassword.clear();
668             AuthCoreCache::instance()->insert(
669                 AuthCoreCache::CacheId(m_id, m_method), cache);
670         }
671
672         //remove secret field from output
673         if (m_method != QLatin1String("password")
674             && filteredData.contains(SSO_KEY_PASSWORD))
675             filteredData.remove(SSO_KEY_PASSWORD);
676
677         pid_t pid = pidOfContext(rd.m_conn, rd.m_msg);
678         QVariantMap encodedData(m_encryptor->
679                                 encodeVariantMap(filteredData, pid));
680         if (m_encryptor->status() != Encryptor::Ok) {
681             replyError(rd.m_conn,
682                        rd.m_msg,
683                        Error::EncryptionFailure,
684                        QString::fromLatin1("Failed to encrypt outgoing message"));
685         } else {
686             encodedData = filterVariantMap(encodedData);
687             arguments << encodedData;
688             rd.m_conn.send(rd.m_msg.createReply(arguments));
689         }
690
691         m_canceled = QString();
692
693         if (m_watcher && !m_watcher->isFinished()) {
694             m_signonui->cancelUiRequest(rd.m_cancelKey);
695             delete m_watcher;
696             m_watcher = 0;
697         }
698         m_queryCredsUiDisplayed = false;
699     }
700     m_canceled = QString();
701     QMetaObject::invokeMethod(this, "startNewRequest", Qt::QueuedConnection);
702 }
703
704 void SignonSessionCore::processStore(const QString &cancelKey, const QVariantMap &data)
705 {
706     Q_UNUSED(cancelKey);
707     TRACE();
708
709     keepInUse();
710     m_passwordUpdate.clear();
711     if (m_id == SIGNOND_NEW_IDENTITY) {
712         BLAME() << "Cannot store without identity";
713         return;
714     }
715     QVariantMap filteredData = data;
716     //do not store username or password
717     filteredData.remove(SSO_KEY_PASSWORD);
718     filteredData.remove(SSO_KEY_USERNAME);
719     filteredData.remove(SSO_ACCESS_CONTROL_TOKENS);
720
721     //store data into db
722     CredentialsDB *db = CredentialsAccessManager::instance()->credentialsDB();
723     Q_ASSERT(db != NULL);
724
725     StoreOperation storeOp(StoreOperation::Blob);
726     storeOp.m_blobData = filteredData;
727     storeOp.m_authMethod = m_method;
728
729     /* If the credentials are validated, the secrets db is not available and
730      * not authorized keys are available inform the CAM about the situation. */
731     SignonIdentityInfo info = db->credentials(m_id);
732     if (info.validated() && !db->isSecretsDBOpen()) {
733         /* Send the storage not available event only if the curent store
734          * processing is following a previous signon UI query. This is to avoid
735          * unexpected UI pop-ups.
736          */
737         if (m_queryCredsUiDisplayed) {
738             TRACE() << "Secure storage not available. Queueing store operations.";
739             m_storeQueue.enqueue(storeOp);
740
741             SecureStorageEvent *event =
742                 new SecureStorageEvent(
743                     (QEvent::Type)SIGNON_SECURE_STORAGE_NOT_AVAILABLE);
744             event->m_sender = static_cast<QObject *>(this);
745
746             QCoreApplication::postEvent(
747                 CredentialsAccessManager::instance(),
748                 event,
749                 Qt::HighEventPriority);
750         }
751     } else {
752         processStoreOperation(storeOp);
753     }
754
755     /* If secrets db not available cache credentials for this session core.
756      * Avoid creating an invalid caching record - cache only if the BLOB data
757      * is not empty. */
758     if (!db->isSecretsDBOpen() && !data.isEmpty()) {
759         TRACE() << "Caching BLOB authentication data.";
760         AuthCache *cache = new AuthCache;
761         cache->setBlobData(data);
762         AuthCoreCache::instance()->insert(
763             AuthCoreCache::CacheId(m_id, m_method), cache);
764     }
765     m_queryCredsUiDisplayed = false;
766
767     return;
768 }
769
770 void SignonSessionCore::processUiRequest(const QString &cancelKey, const QVariantMap &data)
771 {
772     TRACE();
773
774     keepInUse();
775
776     if (cancelKey != m_canceled && m_listOfRequests.size()) {
777         QString uiRequestId = m_listOfRequests.head().m_cancelKey;
778
779         if (m_watcher) {
780             if (!m_watcher->isFinished())
781                 m_signonui->cancelUiRequest(uiRequestId);
782
783             delete m_watcher;
784             m_watcher = 0;
785         }
786
787         m_listOfRequests.head().m_params = filterVariantMap(data);
788         m_listOfRequests.head().m_params[SSOUI_KEY_REQUESTID] = uiRequestId;
789
790         if (m_id == SIGNOND_NEW_IDENTITY)
791             m_listOfRequests.head().m_params[SSOUI_KEY_STORED_IDENTITY] = false;
792         else
793             m_listOfRequests.head().m_params[SSOUI_KEY_STORED_IDENTITY] = true;
794
795         CredentialsAccessManager *camManager = CredentialsAccessManager::instance();
796         CredentialsDB *db = camManager->credentialsDB();
797         Q_ASSERT(db != 0);
798
799         //check that we have caption
800         if (!data.contains(SSO_KEY_CAPTION)) {
801             TRACE() << "Caption missing";
802             if (m_id != SIGNOND_NEW_IDENTITY) {
803                 SignonIdentityInfo info = db->credentials(m_id);
804                 m_listOfRequests.head().m_params.insert(SSO_KEY_CAPTION, info.caption());
805                 TRACE() << "Got caption: " << info.caption();
806             }
807         }
808
809         /*
810          * Check the secure storage status, if any issues are encountered signal
811          * this to the signon ui. */
812         if (!db->isSecretsDBOpen()) {
813             TRACE();
814
815             //If there are no keys available
816             if (!camManager->keysAvailable()) {
817                 TRACE() << "Secrets DB not available."
818                         << "CAM has no keys available. Informing signon-ui.";
819                 m_listOfRequests.head().m_params[
820                     SSOUI_KEY_STORAGE_KEYS_UNAVAILABLE] = true;
821             }
822         }
823
824         m_watcher = new QDBusPendingCallWatcher(m_signonui->queryDialog(m_listOfRequests.head().m_params),
825                                                 this);
826         connect(m_watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(queryUiSlot(QDBusPendingCallWatcher*)));
827     }
828 }
829
830 void SignonSessionCore::processRefreshRequest(const QString &cancelKey, const QVariantMap &data)
831 {
832     TRACE();
833
834     keepInUse();
835
836     if (cancelKey != m_canceled && m_listOfRequests.size()) {
837         QString uiRequestId = m_listOfRequests.head().m_cancelKey;
838
839         if (m_watcher) {
840             if (!m_watcher->isFinished())
841                 m_signonui->cancelUiRequest(uiRequestId);
842
843             delete m_watcher;
844             m_watcher = 0;
845         }
846
847         m_listOfRequests.head().m_params = filterVariantMap(data);
848         m_watcher = new QDBusPendingCallWatcher(m_signonui->refreshDialog(m_listOfRequests.head().m_params),
849                                                 this);
850         connect(m_watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(queryUiSlot(QDBusPendingCallWatcher*)));
851     }
852 }
853
854 void SignonSessionCore::processError(const QString &cancelKey, int err, const QString &message)
855 {
856     TRACE();
857     keepInUse();
858     m_tmpUsername.clear();
859     m_tmpPassword.clear();
860
861     if (!m_listOfRequests.size())
862         return;
863
864     RequestData rd = m_listOfRequests.dequeue();
865
866     if (cancelKey != m_canceled) {
867         replyError(rd.m_conn, rd.m_msg, err, message);
868
869         if (m_watcher && !m_watcher->isFinished()) {
870             m_signonui->cancelUiRequest(rd.m_cancelKey);
871             delete m_watcher;
872             m_watcher = 0;
873         }
874     }
875
876     m_canceled = QString();
877     QMetaObject::invokeMethod(this, "startNewRequest", Qt::QueuedConnection);
878 }
879
880 void SignonSessionCore::stateChangedSlot(const QString &cancelKey, int state, const QString &message)
881 {
882     if (cancelKey != m_canceled && m_listOfRequests.size()) {
883         RequestData rd = m_listOfRequests.head();
884         emit stateChanged(rd.m_cancelKey, (int)state, message);
885     }
886
887     keepInUse();
888 }
889
890 void SignonSessionCore::childEvent(QChildEvent *ce)
891 {
892     if (ce->added())
893         keepInUse();
894     else if (ce->removed())
895         SignonDisposable::destroyUnused();
896 }
897
898 void SignonSessionCore::customEvent(QEvent *event)
899 {
900     TRACE() << "Custom event received.";
901     if (event->type() == SIGNON_SECURE_STORAGE_AVAILABLE) {
902         TRACE() << "Secure storage is available.";
903         AuthCoreCache::instance()->clear();
904
905         TRACE() << "Processing queued stored operations.";
906         while (!m_storeQueue.empty()) {
907             processStoreOperation(m_storeQueue.dequeue());
908         }
909     } else if (event->type() == SIGNON_SECURE_STORAGE_NOT_AVAILABLE) {
910         TRACE() << "Secure storage still not available. "
911                    "Clearing storage operation queue.";
912         m_storeQueue.clear();
913     }
914
915     QObject::customEvent(event);
916 }
917
918 void SignonSessionCore::queryUiSlot(QDBusPendingCallWatcher *call)
919 {
920     keepInUse();
921
922     QDBusPendingReply<QVariantMap> reply = *call;
923     bool isRequestToRefresh = false;
924     Q_ASSERT_X( m_listOfRequests.size() != 0, __func__, "queue of requests is empty");
925
926     if (!reply.isError() && reply.count()) {
927         QVariantMap resultParameters = reply.argumentAt<0>();
928         if (resultParameters.contains(SSOUI_KEY_REFRESH)) {
929             isRequestToRefresh = true;
930             resultParameters.remove(SSOUI_KEY_REFRESH);
931         }
932
933         m_listOfRequests.head().m_params = resultParameters;
934
935         /* If the query ui was canceled or any other error occurred
936          * do not set this flag to true. */
937         if (resultParameters.contains(SSOUI_KEY_ERROR)
938             && (resultParameters[SSOUI_KEY_ERROR] == QUERY_ERROR_CANCELED)) {
939
940             m_queryCredsUiDisplayed = false;
941         } else {
942             m_queryCredsUiDisplayed = true;
943         }
944     } else {
945         m_listOfRequests.head().m_params.insert(SSOUI_KEY_ERROR, (int)SignOn::QUERY_ERROR_NO_SIGNONUI);
946     }
947
948     if (m_listOfRequests.head().m_cancelKey != m_canceled) {
949         /* Temporary caching, if credentials are valid
950          * this data will be effectively cached */
951         m_tmpUsername = m_listOfRequests.head().m_params.value(
952             SSO_KEY_USERNAME, QVariant()).toString();
953         m_tmpPassword = m_listOfRequests.head().m_params.value(
954             SSO_KEY_PASSWORD, QVariant()).toString();
955
956         if (isRequestToRefresh) {
957             TRACE() << "REFRESH IS REQUIRED";
958
959             m_listOfRequests.head().m_params.remove(SSOUI_KEY_REFRESH);
960             m_plugin->processRefresh(m_listOfRequests.head().m_cancelKey,
961                                      m_listOfRequests.head().m_params);
962         } else {
963             if (m_listOfRequests.head().m_params.contains(SSO_KEY_PASSWORD))
964                 m_passwordUpdate = m_listOfRequests.head().m_params[SSO_KEY_PASSWORD].toString();
965             m_plugin->processUi(m_listOfRequests.head().m_cancelKey,
966                                 m_listOfRequests.head().m_params);
967         }
968     }
969
970     delete m_watcher;
971     m_watcher = NULL;
972 }
973
974 void SignonSessionCore::startNewRequest()
975 {
976     keepInUse();
977
978     // there is no request
979     if (!m_listOfRequests.length()) {
980         TRACE() << "the data queue is EMPTY!!!";
981         return;
982     }
983
984     //plugin is busy
985     if (m_plugin && m_plugin->isProcessing()) {
986         TRACE() << " the plugin is in challenge processing";
987         return;
988     }
989
990     //there is some UI operation with plugin
991     if (m_watcher && !m_watcher->isFinished()) {
992         TRACE() << "watcher is in running mode";
993         return;
994     }
995
996     TRACE() << "Start the authentication process";
997     startProcess();
998 }
999
1000 void SignonSessionCore::destroy()
1001 {
1002     if (m_plugin->isProcessing() ||
1003         m_watcher != NULL) {
1004         keepInUse();
1005         return;
1006     }
1007
1008     if (m_id)
1009         sessionsOfStoredCredentials.remove(sessionName(m_id, m_method));
1010     else
1011         sessionsOfNonStoredCredentials.removeOne(this);
1012
1013     emit destroyed();
1014     deleteLater();
1015 }
1016
1017 void SignonSessionCore::addRef()
1018 {
1019     TRACE();
1020     m_refCount++;
1021     setAutoDestruct(false);
1022 }
1023
1024 void SignonSessionCore::removeRef()
1025 {
1026     TRACE();
1027     Q_ASSERT(m_refCount <= 0);
1028     m_refCount--;
1029     if (m_refCount == 0) {
1030         TRACE();
1031         setAutoDestruct(true);
1032     }
1033 }
1034
1035 void SignonSessionCore::credentialsSystemReady()
1036 {
1037     QMetaObject::invokeMethod(this, "startNewRequest", Qt::QueuedConnection);
1038 }