1/*
2 * Copyright (C) 2012 Research In Motion Limited. All rights reserved.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17 */
18
19#include "config.h"
20#include "AuthenticationChallengeManager.h"
21
22#include "Credential.h"
23#include "KURL.h"
24#include "PageClientBlackBerry.h"
25#include "ProtectionSpace.h"
26
27#include <BlackBerryPlatformAssert.h>
28#include <BlackBerryPlatformLog.h>
29#include <wtf/Assertions.h>
30#include <wtf/HashMap.h>
31#include <wtf/PassOwnPtr.h>
32#include <wtf/Vector.h>
33#include <wtf/text/CString.h>
34
35namespace WebCore {
36
37typedef HashMap<PageClientBlackBerry*, bool> PageVisibilityMap;
38
39struct ChallengeInfo {
40    ChallengeInfo(const KURL&, const ProtectionSpace&, const Credential&, AuthenticationChallengeClient*, PageClientBlackBerry*);
41
42    KURL url;
43    ProtectionSpace space;
44    Credential credential;
45    AuthenticationChallengeClient* authClient;
46    PageClientBlackBerry* pageClient;
47    bool blocked;
48};
49
50ChallengeInfo::ChallengeInfo(const KURL& aUrl, const ProtectionSpace& aSpace, const Credential& aCredential,
51    AuthenticationChallengeClient* anAuthClient, PageClientBlackBerry* aPageClient)
52    : url(aUrl)
53    , space(aSpace)
54    , credential(aCredential)
55    , authClient(anAuthClient)
56    , pageClient(aPageClient)
57    , blocked(false)
58{
59}
60
61class AuthenticationChallengeManagerPrivate {
62public:
63    AuthenticationChallengeManagerPrivate();
64
65    bool resumeAuthenticationChallenge(PageClientBlackBerry*);
66    void startAuthenticationChallenge(ChallengeInfo*);
67    bool pageExists(PageClientBlackBerry*);
68
69    ChallengeInfo* m_activeChallenge;
70    PageVisibilityMap m_pageVisibilityMap;
71    Vector<OwnPtr<ChallengeInfo> > m_challenges;
72};
73
74AuthenticationChallengeManagerPrivate::AuthenticationChallengeManagerPrivate()
75    : m_activeChallenge(0)
76{
77}
78
79bool AuthenticationChallengeManagerPrivate::resumeAuthenticationChallenge(PageClientBlackBerry* client)
80{
81    ASSERT(!m_activeChallenge);
82
83    for (size_t i = 0; i < m_challenges.size(); ++i) {
84        if (m_challenges[i]->pageClient == client && m_challenges[i]->blocked) {
85            startAuthenticationChallenge(m_challenges[i].get());
86            return true;
87        }
88    }
89
90    return false;
91}
92
93void AuthenticationChallengeManagerPrivate::startAuthenticationChallenge(ChallengeInfo* info)
94{
95    m_activeChallenge = info;
96    m_activeChallenge->blocked = false;
97    m_activeChallenge->pageClient->authenticationChallenge(m_activeChallenge->url, m_activeChallenge->space, m_activeChallenge->credential);
98}
99
100bool AuthenticationChallengeManagerPrivate::pageExists(PageClientBlackBerry* client)
101{
102    return m_pageVisibilityMap.find(client) != m_pageVisibilityMap.end();
103}
104
105SINGLETON_INITIALIZER_THREADUNSAFE(AuthenticationChallengeManager)
106
107AuthenticationChallengeManager::AuthenticationChallengeManager()
108    : d(adoptPtr(new AuthenticationChallengeManagerPrivate))
109{
110}
111
112void AuthenticationChallengeManager::pageCreated(PageClientBlackBerry* client)
113{
114    d->m_pageVisibilityMap.add(client, true);
115}
116
117void AuthenticationChallengeManager::pageDeleted(PageClientBlackBerry* client)
118{
119    d->m_pageVisibilityMap.remove(client);
120
121    if (d->m_activeChallenge && d->m_activeChallenge->pageClient == client)
122        d->m_activeChallenge = 0;
123
124    Vector<OwnPtr<ChallengeInfo> > existing;
125    d->m_challenges.swap(existing);
126
127    for (size_t i = 0; i < existing.size(); ++i) {
128        if (existing[i]->pageClient != client)
129            d->m_challenges.append(existing[i].release());
130    }
131}
132
133void AuthenticationChallengeManager::pageVisibilityChanged(PageClientBlackBerry* client, bool visible)
134{
135    PageVisibilityMap::iterator iter = d->m_pageVisibilityMap.find(client);
136
137    ASSERT(iter != d->m_pageVisibilityMap.end());
138    if (iter == d->m_pageVisibilityMap.end()) {
139        d->m_pageVisibilityMap.add(client, visible);
140        return;
141    }
142
143    if (iter->value == visible)
144        return;
145
146    iter->value = visible;
147    if (!visible)
148        return;
149
150    if (d->m_activeChallenge)
151        return;
152
153    d->resumeAuthenticationChallenge(client);
154}
155
156void AuthenticationChallengeManager::authenticationChallenge(const KURL& url, const ProtectionSpace& space,
157    const Credential& credential, AuthenticationChallengeClient* authClient, PageClientBlackBerry* pageClient)
158{
159    BLACKBERRY_ASSERT(authClient);
160    BLACKBERRY_ASSERT(pageClient);
161
162    ChallengeInfo* info = new ChallengeInfo(url, space, credential, authClient, pageClient);
163    d->m_challenges.append(adoptPtr(info));
164
165    if (d->m_activeChallenge || !pageClient->isVisible()) {
166        info->blocked = true;
167        return;
168    }
169
170    d->startAuthenticationChallenge(info);
171}
172
173void AuthenticationChallengeManager::cancelAuthenticationChallenge(AuthenticationChallengeClient* client)
174{
175    BLACKBERRY_ASSERT(client);
176
177    if (d->m_activeChallenge && d->m_activeChallenge->authClient == client)
178        d->m_activeChallenge = 0;
179
180    Vector<OwnPtr<ChallengeInfo> > existing;
181    d->m_challenges.swap(existing);
182
183    ChallengeInfo* next = 0;
184    PageClientBlackBerry* page = 0;
185
186    for (size_t i = 0; i < existing.size(); ++i) {
187        if (existing[i]->authClient != client) {
188            if (page && !next && existing[i]->pageClient == page)
189                next = existing[i].get();
190            d->m_challenges.append(existing[i].release());
191        } else if (d->m_activeChallenge == existing[i].get())
192            page = existing[i]->pageClient;
193    }
194
195    if (next)
196        d->startAuthenticationChallenge(next);
197}
198
199void AuthenticationChallengeManager::notifyChallengeResult(const KURL&, const ProtectionSpace& space,
200    AuthenticationChallengeResult result, const Credential& credential)
201{
202    d->m_activeChallenge = 0;
203
204    Vector<OwnPtr<ChallengeInfo> > existing;
205    d->m_challenges.swap(existing);
206
207    ChallengeInfo* next = 0;
208    PageClientBlackBerry* page = 0;
209
210    for (size_t i = 0; i < existing.size(); ++i) {
211        if (existing[i]->space != space) {
212            if (page && !next && existing[i]->pageClient == page)
213                next = existing[i].get();
214            d->m_challenges.append(existing[i].release());
215        } else {
216            page = existing[i]->pageClient;
217            existing[i]->authClient->notifyChallengeResult(existing[i]->url, space, result, credential);
218
219            // After calling notifyChallengeResult(), page could be destroyed or something.
220            if (!d->pageExists(page) || !page->isVisible())
221                page = 0;
222        }
223    }
224
225    if (next)
226        d->startAuthenticationChallenge(next);
227}
228
229} // namespace WebCore
230