1/*
2 * Copyright (C) 2005 Apple Computer, Inc.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1.  Redistributions of source code must retain the above copyright
9 *     notice, this list of conditions and the following disclaimer.
10 * 2.  Redistributions in binary form must reproduce the above copyright
11 *     notice, this list of conditions and the following disclaimer in the
12 *     documentation and/or other materials provided with the distribution.
13 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 *     its contributors may be used to endorse or promote products derived
15 *     from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#import <WebKit/WebPanelAuthenticationHandler.h>
30
31#import <Foundation/NSURLAuthenticationChallenge.h>
32#import <WebKit/WebAuthenticationPanel.h>
33#import <WebKit/WebNSDictionaryExtras.h>
34#import <wtf/Assertions.h>
35
36static NSString *WebModalDialogPretendWindow = @"WebModalDialogPretendWindow";
37
38@implementation WebPanelAuthenticationHandler
39
40WebPanelAuthenticationHandler *sharedHandler;
41
42+ (id)sharedHandler
43{
44    if (sharedHandler == nil)
45        sharedHandler = [[self alloc] init];
46    return sharedHandler;
47}
48
49-(id)init
50{
51    self = [super init];
52    if (self != nil) {
53        windowToPanel = [[NSMutableDictionary alloc] init];
54        challengeToWindow = [[NSMutableDictionary alloc] init];
55        windowToChallengeQueue = [[NSMutableDictionary alloc] init];
56    }
57    return self;
58}
59
60-(void)dealloc
61{
62    [windowToPanel release];
63    [challengeToWindow release];
64    [windowToChallengeQueue release];
65    [super dealloc];
66}
67
68-(void)enqueueChallenge:(NSURLAuthenticationChallenge *)challenge forWindow:(id)window
69{
70    NSMutableArray *queue = [windowToChallengeQueue objectForKey:window];
71    if (queue == nil) {
72        queue = [[NSMutableArray alloc] init];
73        [windowToChallengeQueue _webkit_setObject:queue forUncopiedKey:window];
74        [queue release];
75    }
76    [queue addObject:challenge];
77}
78
79-(void)tryNextChallengeForWindow:(id)window
80{
81    NSMutableArray *queue = [windowToChallengeQueue objectForKey:window];
82    if (queue == nil) {
83        return;
84    }
85
86    NSURLAuthenticationChallenge *challenge = [[queue objectAtIndex:0] retain];
87    [queue removeObjectAtIndex:0];
88    if ([queue count] == 0) {
89        [windowToChallengeQueue removeObjectForKey:window];
90    }
91
92    NSURLCredential *latestCredential = [[NSURLCredentialStorage sharedCredentialStorage] defaultCredentialForProtectionSpace:[challenge protectionSpace]];
93
94    if ([latestCredential hasPassword]) {
95        [[challenge sender] useCredential:latestCredential forAuthenticationChallenge:challenge];
96        [challenge release];
97        return;
98    }
99
100    [self startAuthentication:challenge window:(window == WebModalDialogPretendWindow ? nil : window)];
101    [challenge release];
102}
103
104
105-(void)startAuthentication:(NSURLAuthenticationChallenge *)challenge window:(NSWindow *)w
106{
107    id window = w ? (id)w : (id)WebModalDialogPretendWindow;
108
109    if ([windowToPanel objectForKey:window] != nil) {
110        [self enqueueChallenge:challenge forWindow:window];
111        return;
112    }
113
114    // In this case, we have an attached sheet that's not one of our
115    // authentication panels, so enqueing is not an option. Just
116    // cancel loading instead, since this case is fairly
117    // unlikely (how would you be loading a page if you had an error
118    // sheet up?)
119    if ([w attachedSheet] != nil) {
120        [[challenge sender] cancelAuthenticationChallenge:challenge];
121        return;
122    }
123
124    WebAuthenticationPanel *panel = [[WebAuthenticationPanel alloc] initWithCallback:self selector:@selector(_authenticationDoneWithChallenge:result:)];
125    [challengeToWindow _webkit_setObject:window forUncopiedKey:challenge];
126    [windowToPanel _webkit_setObject:panel forUncopiedKey:window];
127    [panel release];
128
129    if (window == WebModalDialogPretendWindow) {
130        [panel runAsModalDialogWithChallenge:challenge];
131    } else {
132        [panel runAsSheetOnWindow:window withChallenge:challenge];
133    }
134}
135
136-(void)cancelAuthentication:(NSURLAuthenticationChallenge *)challenge
137{
138    id window = [challengeToWindow objectForKey:challenge];
139    if (window != nil) {
140        WebAuthenticationPanel *panel = [windowToPanel objectForKey:window];
141        [panel cancel:self];
142    }
143}
144
145-(void)_authenticationDoneWithChallenge:(NSURLAuthenticationChallenge *)challenge result:(NSURLCredential *)credential
146{
147    id window = [challengeToWindow objectForKey:challenge];
148    [window retain];
149    if (window != nil) {
150        [windowToPanel removeObjectForKey:window];
151        [challengeToWindow removeObjectForKey:challenge];
152    }
153
154    if (credential == nil) {
155        [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
156    } else {
157        [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
158    }
159
160    [self tryNextChallengeForWindow:window];
161    [window release];
162}
163
164@end
165