1/*
2 * Copyright (C) 2013 Samsung Electronics Inc. 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 Library 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 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB.  If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20#include "config.h"
21#include "WebKitAuthenticationRequest.h"
22
23#include "AuthenticationDecisionListener.h"
24#include "WebCredential.h"
25#include "WebKitAuthenticationRequestPrivate.h"
26#include "WebKitCredentialPrivate.h"
27#include "WebProtectionSpace.h"
28#include <glib/gi18n-lib.h>
29#include <wtf/text/CString.h>
30
31using namespace WebKit;
32using namespace WebCore;
33
34/**
35 * SECTION: WebKitAuthenticationRequest
36 * @Short_description: Represents an authentication request
37 * @Title: WebKitAuthenticationRequest
38 * @See_also: #WebKitWebView
39 *
40 * Whenever a client attempts to load a page protected by HTTP
41 * authentication, credentials will need to be provided to authorize access.
42 * To allow the client to decide how it wishes to handle authentication,
43 * WebKit will fire a #WebKitWebView::authenticate signal with a
44 * WebKitAuthenticationRequest object to provide client side
45 * authentication support. Credentials are exposed through the
46 * #WebKitCredential object.
47 *
48 * In case the client application does not wish
49 * to handle this signal WebKit will provide a default handler. To handle
50 * authentication asynchronously, simply increase the reference count of the
51 * WebKitAuthenticationRequest object.
52 */
53
54enum {
55    CANCELLED,
56
57    LAST_SIGNAL
58};
59
60struct _WebKitAuthenticationRequestPrivate {
61    RefPtr<AuthenticationChallengeProxy> authenticationChallenge;
62    bool privateBrowsingEnabled;
63    bool handledRequest;
64    CString host;
65    CString realm;
66};
67
68static guint signals[LAST_SIGNAL] = { 0, };
69
70WEBKIT_DEFINE_TYPE(WebKitAuthenticationRequest, webkit_authentication_request, G_TYPE_OBJECT)
71
72static inline WebKitAuthenticationScheme toWebKitAuthenticationScheme(WebCore::ProtectionSpaceAuthenticationScheme coreScheme)
73{
74    switch (coreScheme) {
75    case WebCore::ProtectionSpaceAuthenticationSchemeDefault:
76        return WEBKIT_AUTHENTICATION_SCHEME_DEFAULT;
77    case WebCore::ProtectionSpaceAuthenticationSchemeHTTPBasic:
78        return WEBKIT_AUTHENTICATION_SCHEME_HTTP_BASIC;
79    case WebCore::ProtectionSpaceAuthenticationSchemeHTTPDigest:
80        return WEBKIT_AUTHENTICATION_SCHEME_HTTP_DIGEST;
81    case WebCore::ProtectionSpaceAuthenticationSchemeHTMLForm:
82        return WEBKIT_AUTHENTICATION_SCHEME_HTML_FORM;
83    case WebCore::ProtectionSpaceAuthenticationSchemeNTLM:
84        return WEBKIT_AUTHENTICATION_SCHEME_NTLM;
85    case WebCore::ProtectionSpaceAuthenticationSchemeNegotiate:
86        return WEBKIT_AUTHENTICATION_SCHEME_NEGOTIATE;
87    case WebCore::ProtectionSpaceAuthenticationSchemeClientCertificateRequested:
88        return WEBKIT_AUTHENTICATION_SCHEME_CLIENT_CERTIFICATE_REQUESTED;
89    case WebCore::ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested:
90        return WEBKIT_AUTHENTICATION_SCHEME_SERVER_TRUST_EVALUATION_REQUESTED;
91    case WebCore::ProtectionSpaceAuthenticationSchemeUnknown:
92        return WEBKIT_AUTHENTICATION_SCHEME_UNKNOWN;
93    default:
94        ASSERT_NOT_REACHED();
95        return WEBKIT_AUTHENTICATION_SCHEME_DEFAULT;
96    }
97}
98
99static void webkitAuthenticationRequestDispose(GObject* object)
100{
101    WebKitAuthenticationRequest* request = WEBKIT_AUTHENTICATION_REQUEST(object);
102
103    // Make sure the request is always handled before finalizing.
104    if (!request->priv->handledRequest)
105        webkit_authentication_request_cancel(request);
106
107    G_OBJECT_CLASS(webkit_authentication_request_parent_class)->dispose(object);
108}
109
110static void webkit_authentication_request_class_init(WebKitAuthenticationRequestClass* requestClass)
111{
112    GObjectClass* objectClass = G_OBJECT_CLASS(requestClass);
113    objectClass->dispose = webkitAuthenticationRequestDispose;
114
115    /**
116     * WebKitAuthenticationRequest::cancelled:
117     * @request: the #WebKitAuthenticationRequest
118     *
119     * This signal is emitted when the user authentication request is
120     * cancelled. It allows the application to dismiss its authentication
121     * dialog in case of page load failure for example.
122     *
123     * Since: 2.2
124     */
125    signals[CANCELLED] =
126        g_signal_new("cancelled",
127            G_TYPE_FROM_CLASS(objectClass),
128            G_SIGNAL_RUN_LAST,
129            0, 0, 0,
130            g_cclosure_marshal_VOID__VOID,
131            G_TYPE_NONE, 0);
132}
133
134WebKitAuthenticationRequest* webkitAuthenticationRequestCreate(AuthenticationChallengeProxy* authenticationChallenge, bool privateBrowsingEnabled)
135{
136    WebKitAuthenticationRequest* request = WEBKIT_AUTHENTICATION_REQUEST(g_object_new(WEBKIT_TYPE_AUTHENTICATION_REQUEST, NULL));
137    request->priv->authenticationChallenge = authenticationChallenge;
138    request->priv->privateBrowsingEnabled = privateBrowsingEnabled;
139    return request;
140}
141
142AuthenticationChallengeProxy* webkitAuthenticationRequestGetAuthenticationChallenge(WebKitAuthenticationRequest* request)
143{
144    return request->priv->authenticationChallenge.get();
145}
146
147/**
148 * webkit_authentication_request_can_save_credentials:
149 * @request: a #WebKitAuthenticationRequest
150 *
151 * Determine whether the authentication method associated with this
152 * #WebKitAuthenticationRequest should allow the storage of credentials.
153 * This will return %FALSE if webkit doesn't support credential storing
154 * or if private browsing is enabled.
155 *
156 * Returns: %TRUE if webkit can store credentials or %FALSE otherwise.
157 *
158 * Since: 2.2
159 */
160gboolean webkit_authentication_request_can_save_credentials(WebKitAuthenticationRequest* request)
161{
162    g_return_val_if_fail(WEBKIT_IS_AUTHENTICATION_REQUEST(request), FALSE);
163
164#if ENABLE(CREDENTIAL_STORAGE)
165    return !request->priv->privateBrowsingEnabled;
166#else
167    return FALSE;
168#endif
169}
170
171/**
172 * webkit_authentication_request_get_proposed_credential:
173 * @request: a #WebKitAuthenticationRequest
174 *
175 * Get the #WebKitCredential of the proposed authentication challenge that was
176 * stored from a previous session. The client can use this directly for
177 * authentication or construct their own #WebKitCredential.
178 *
179 * Returns: (transfer full): A #WebKitCredential encapsulating credential details
180 * or %NULL if there is no stored credential.
181 *
182 * Since: 2.2
183 */
184WebKitCredential* webkit_authentication_request_get_proposed_credential(WebKitAuthenticationRequest* request)
185{
186    g_return_val_if_fail(WEBKIT_IS_AUTHENTICATION_REQUEST(request), 0);
187
188    const WebCore::Credential& credential = request->priv->authenticationChallenge->proposedCredential()->credential();
189    if (credential.isEmpty())
190        return 0;
191
192    return webkitCredentialCreate(credential);
193}
194
195/**
196 * webkit_authentication_request_get_host:
197 * @request: a #WebKitAuthenticationRequest
198 *
199 * Get the host that this authentication challenge is applicable to.
200 *
201 * Returns: The host of @request.
202 *
203 * Since: 2.2
204 */
205const gchar* webkit_authentication_request_get_host(WebKitAuthenticationRequest* request)
206{
207    g_return_val_if_fail(WEBKIT_IS_AUTHENTICATION_REQUEST(request), 0);
208
209    if (request->priv->host.isNull())
210        request->priv->host = request->priv->authenticationChallenge->protectionSpace()->host().utf8();
211    return request->priv->host.data();
212}
213
214/**
215 * webkit_authentication_request_get_port:
216 * @request: a #WebKitAuthenticationRequest
217 *
218 * Get the port that this authentication challenge is applicable to.
219 *
220 * Returns: The port of @request.
221 *
222 * Since: 2.2
223 */
224guint webkit_authentication_request_get_port(WebKitAuthenticationRequest* request)
225{
226    g_return_val_if_fail(WEBKIT_IS_AUTHENTICATION_REQUEST(request), 0);
227
228    return request->priv->authenticationChallenge->protectionSpace()->port();
229}
230
231/**
232 * webkit_authentication_request_get_realm:
233 * @request: a #WebKitAuthenticationRequest
234 *
235 * Get the realm that this authentication challenge is applicable to.
236 *
237 * Returns: The realm of @request.
238 *
239 * Since: 2.2
240 */
241const gchar* webkit_authentication_request_get_realm(WebKitAuthenticationRequest* request)
242{
243    g_return_val_if_fail(WEBKIT_IS_AUTHENTICATION_REQUEST(request), 0);
244
245    if (request->priv->realm.isNull())
246        request->priv->realm = request->priv->authenticationChallenge->protectionSpace()->realm().utf8();
247    return request->priv->realm.data();
248}
249
250/**
251 * webkit_authentication_request_get_scheme:
252 * @request: a #WebKitAuthenticationRequest
253 *
254 * Get the authentication scheme of the authentication challenge.
255 *
256 * Returns: The #WebKitAuthenticationScheme of @request.
257 *
258 * Since: 2.2
259 */
260WebKitAuthenticationScheme webkit_authentication_request_get_scheme(WebKitAuthenticationRequest* request)
261{
262    g_return_val_if_fail(WEBKIT_IS_AUTHENTICATION_REQUEST(request), WEBKIT_AUTHENTICATION_SCHEME_UNKNOWN);
263
264    return toWebKitAuthenticationScheme(request->priv->authenticationChallenge->protectionSpace()->authenticationScheme());
265}
266
267/**
268 * webkit_authentication_request_is_for_proxy:
269 * @request: a #WebKitAuthenticationRequest
270 *
271 * Determine whether the authentication challenge is associated with a proxy server rather than an "origin" server.
272 *
273 * Returns: %TRUE if authentication is for a proxy or %FALSE otherwise.
274 *
275 * Since: 2.2
276 */
277gboolean webkit_authentication_request_is_for_proxy(WebKitAuthenticationRequest* request)
278{
279    g_return_val_if_fail(WEBKIT_IS_AUTHENTICATION_REQUEST(request), FALSE);
280
281    return request->priv->authenticationChallenge->protectionSpace()->isProxy();
282}
283
284/**
285 * webkit_authentication_request_is_retry:
286 * @request: a #WebKitAuthenticationRequest
287 *
288 * Determine whether this this is a first attempt or a retry for this authentication challenge.
289 *
290 * Returns: %TRUE if authentication attempt is a retry or %FALSE otherwise.
291 *
292 * Since: 2.2
293 */
294gboolean webkit_authentication_request_is_retry(WebKitAuthenticationRequest* request)
295{
296    g_return_val_if_fail(WEBKIT_IS_AUTHENTICATION_REQUEST(request), 0);
297
298    return request->priv->authenticationChallenge->previousFailureCount() ? TRUE : FALSE;
299}
300
301/**
302 * webkit_authentication_request_authenticate:
303 * @request: a #WebKitAuthenticationRequest
304 * @credential: (transfer none) (allow-none): A #WebKitCredential, or %NULL
305 *
306 * Authenticate the #WebKitAuthenticationRequest using the #WebKitCredential
307 * supplied. To continue without credentials, pass %NULL as @credential.
308 *
309 * Since: 2.2
310 */
311void webkit_authentication_request_authenticate(WebKitAuthenticationRequest* request, WebKitCredential* credential)
312{
313    g_return_if_fail(WEBKIT_IS_AUTHENTICATION_REQUEST(request));
314
315    RefPtr<WebCredential> webCredential = credential ? WebCredential::create(webkitCredentialGetCredential(credential)) : 0;
316    request->priv->authenticationChallenge->listener()->useCredential(webCredential.get());
317    request->priv->handledRequest = true;
318}
319
320/**
321 * webkit_authentication_request_cancel:
322 * @request: a #WebKitAuthenticationRequest
323 *
324 * Cancel the authentication challenge. This will also cancel the page loading and result in a
325 * #WebKitWebView::load-failed signal with a #WebKitNetworkError of type %WEBKIT_NETWORK_ERROR_CANCELLED being emitted.
326 *
327 * Since: 2.2
328 */
329void webkit_authentication_request_cancel(WebKitAuthenticationRequest* request)
330{
331    g_return_if_fail(WEBKIT_IS_AUTHENTICATION_REQUEST(request));
332
333    request->priv->authenticationChallenge->listener()->cancel();
334
335    g_signal_emit(request, signals[CANCELLED], 0);
336}
337