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 36 static NSString *WebModalDialogPretendWindow = @"WebModalDialogPretendWindow"; 37 38 @implementation WebPanelAuthenticationHandler 39 40 WebPanelAuthenticationHandler *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