WebAuthenticationPanel.m 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  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. #import <WebKit/WebAuthenticationPanel.h>
  29. #import "WebLocalizableStringsInternal.h"
  30. #import <Foundation/NSURLAuthenticationChallenge.h>
  31. #import <Foundation/NSURLProtectionSpace.h>
  32. #import <Foundation/NSURLCredential.h>
  33. #import <WebKit/WebKitNSStringExtras.h>
  34. #import <WebKit/WebNSURLExtras.h>
  35. #import <wtf/Assertions.h>
  36. #import <WebKit/WebNSControlExtras.h>
  37. #define WebAuthenticationPanelNibName @"WebAuthenticationPanel"
  38. @implementation WebAuthenticationPanel
  39. -(id)initWithCallback:(id)cb selector:(SEL)sel
  40. {
  41. self = [self init];
  42. if (self != nil) {
  43. callback = [cb retain];
  44. selector = sel;
  45. }
  46. return self;
  47. }
  48. - (void)dealloc
  49. {
  50. [panel release];
  51. [callback release];
  52. [super dealloc];
  53. }
  54. // IB actions
  55. - (IBAction)cancel:(id)sender
  56. {
  57. // This is required because the body of this method is going to
  58. // remove all of the panel's remaining refs, which can cause a
  59. // crash later when finishing button hit tracking. So we make
  60. // sure it lives on a bit longer.
  61. [[panel retain] autorelease];
  62. // This is required as a workaround for AppKit issue 4118422
  63. [[self retain] autorelease];
  64. [panel close];
  65. if (usingSheet) {
  66. [[NSApplication sharedApplication] endSheet:panel returnCode:1];
  67. } else {
  68. [[NSApplication sharedApplication] stopModalWithCode:1];
  69. }
  70. }
  71. - (IBAction)logIn:(id)sender
  72. {
  73. // This is required because the body of this method is going to
  74. // remove all of the panel's remaining refs, which can cause a
  75. // crash later when finishing button hit tracking. So we make
  76. // sure it lives on a bit longer.
  77. [[panel retain] autorelease];
  78. [panel close];
  79. if (usingSheet) {
  80. [[NSApplication sharedApplication] endSheet:panel returnCode:0];
  81. } else {
  82. [[NSApplication sharedApplication] stopModalWithCode:0];
  83. }
  84. }
  85. - (BOOL)loadNib
  86. {
  87. if (!nibLoaded) {
  88. #pragma clang diagnostic push
  89. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  90. if ([NSBundle loadNibNamed:WebAuthenticationPanelNibName owner:self]) {
  91. #pragma clang diagnostic pop
  92. nibLoaded = YES;
  93. [imageView setImage:[NSImage imageNamed:@"NSApplicationIcon"]];
  94. } else {
  95. LOG_ERROR("couldn't load nib named '%@'", WebAuthenticationPanelNibName);
  96. return FALSE;
  97. }
  98. }
  99. return TRUE;
  100. }
  101. // Methods related to displaying the panel
  102. -(void)setUpForChallenge:(NSURLAuthenticationChallenge *)chall
  103. {
  104. [self loadNib];
  105. NSURLProtectionSpace *space = [chall protectionSpace];
  106. NSString *host;
  107. if ([space port] == 0) {
  108. host = [[space host] _web_decodeHostName];
  109. } else {
  110. host = [NSString stringWithFormat:@"%@:%ld", [[space host] _web_decodeHostName], (long)[space port]];
  111. }
  112. NSString *realm = [space realm];
  113. if (!realm)
  114. realm = @"";
  115. NSString *message;
  116. // Consider the realm name to be "simple" if it does not contain any whitespace or newline characters.
  117. // If the realm name is determined to be complex, we will use a slightly different sheet layout, designed
  118. // to keep a malicious realm name from spoofing the wording in the sheet text.
  119. BOOL realmNameIsSimple = [realm rangeOfCharacterFromSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].location == NSNotFound;
  120. if ([chall previousFailureCount] == 0) {
  121. if ([space isProxy]) {
  122. message = [NSString stringWithFormat:UI_STRING_INTERNAL("To view this page, you must log in to the %@ proxy server %@.",
  123. "prompt string in authentication panel"),
  124. [space proxyType], host];
  125. } else {
  126. if (realmNameIsSimple)
  127. message = [NSString stringWithFormat:UI_STRING_INTERNAL("To view this page, you must log in to area “%@” on %@.",
  128. "prompt string in authentication panel"), realm, host];
  129. else
  130. message = [NSString stringWithFormat:UI_STRING_INTERNAL("To view this page, you must log in to this area on %@:",
  131. "prompt string in authentication panel"), host];
  132. }
  133. } else {
  134. if ([space isProxy]) {
  135. message = [NSString stringWithFormat:UI_STRING_INTERNAL("The user name or password you entered for the %@ proxy server %@ was incorrect. Make sure you’re entering them correctly, and then try again.",
  136. "prompt string in authentication panel"),
  137. [space proxyType], host];
  138. } else {
  139. if (realmNameIsSimple)
  140. message = [NSString stringWithFormat:UI_STRING_INTERNAL("The user name or password you entered for area “%@” on %@ was incorrect. Make sure you’re entering them correctly, and then try again.",
  141. "prompt string in authentication panel"), realm, host];
  142. else
  143. message = [NSString stringWithFormat:UI_STRING_INTERNAL("The user name or password you entered for this area on %@ was incorrect. Make sure you’re entering them correctly, and then try again.",
  144. "prompt string in authentication panel"), host];
  145. }
  146. }
  147. if (![space isProxy] && !realmNameIsSimple) {
  148. [separateRealmLabel setHidden:NO];
  149. [separateRealmLabel setStringValue:realm];
  150. [separateRealmLabel setAutoresizingMask:NSViewMinYMargin];
  151. [separateRealmLabel sizeToFitAndAdjustWindowHeight];
  152. [separateRealmLabel setAutoresizingMask:NSViewMaxYMargin];
  153. } else {
  154. // In the proxy or "simple" realm name case, we need to hide the 'separateRealmLabel'
  155. // and move the rest of the contents up appropriately to fill the space.
  156. NSRect mainLabelFrame = [mainLabel frame];
  157. NSRect realmFrame = [separateRealmLabel frame];
  158. NSRect smallLabelFrame = [smallLabel frame];
  159. // Find the distance between the 'smallLabel' and the label above it, initially the 'separateRealmLabel'.
  160. // Then, find the current distance between 'smallLabel' and 'mainLabel'. The difference between
  161. // these two is how much shorter the panel needs to be after hiding the 'separateRealmLabel'.
  162. CGFloat smallLabelMargin = NSMinY(realmFrame) - NSMaxY(smallLabelFrame);
  163. CGFloat smallLabelToMainLabel = NSMinY(mainLabelFrame) - NSMaxY(smallLabelFrame);
  164. CGFloat deltaMargin = smallLabelToMainLabel - smallLabelMargin;
  165. [separateRealmLabel setHidden:YES];
  166. NSRect windowFrame = [panel frame];
  167. windowFrame.size.height -= deltaMargin;
  168. [panel setFrame:windowFrame display:NO];
  169. }
  170. [mainLabel setStringValue:message];
  171. [mainLabel sizeToFitAndAdjustWindowHeight];
  172. if ([space receivesCredentialSecurely] || [[space protocol] _webkit_isCaseInsensitiveEqualToString:@"https"]) {
  173. [smallLabel setStringValue:
  174. UI_STRING_INTERNAL("Your login information will be sent securely.",
  175. "message in authentication panel")];
  176. } else {
  177. // Use this scary-sounding phrase only when using basic auth with non-https servers. In this case the password
  178. // could be sniffed by intercepting the network traffic.
  179. [smallLabel setStringValue:
  180. UI_STRING_INTERNAL("Your password will be sent unencrypted.",
  181. "message in authentication panel")];
  182. }
  183. if ([[chall proposedCredential] user] != nil) {
  184. [username setStringValue:[[chall proposedCredential] user]];
  185. [panel setInitialFirstResponder:password];
  186. } else {
  187. [username setStringValue:@""];
  188. [password setStringValue:@""];
  189. [panel setInitialFirstResponder:username];
  190. }
  191. }
  192. - (void)runAsModalDialogWithChallenge:(NSURLAuthenticationChallenge *)chall
  193. {
  194. [self setUpForChallenge:chall];
  195. usingSheet = FALSE;
  196. [chall retain];
  197. NSURLCredential *credential = nil;
  198. if ([[NSApplication sharedApplication] runModalForWindow:panel] == 0) {
  199. credential = [[NSURLCredential alloc] initWithUser:[username stringValue] password:[password stringValue] persistence:([remember state] == NSOnState) ? NSURLCredentialPersistencePermanent : NSURLCredentialPersistenceForSession];
  200. }
  201. [callback performSelector:selector withObject:chall withObject:credential];
  202. [credential release];
  203. [chall release];
  204. }
  205. - (void)runAsSheetOnWindow:(NSWindow *)window withChallenge:(NSURLAuthenticationChallenge *)chall
  206. {
  207. ASSERT(!usingSheet);
  208. [self setUpForChallenge:chall];
  209. usingSheet = TRUE;
  210. challenge = [chall retain];
  211. [[NSApplication sharedApplication] beginSheet:panel modalForWindow:window modalDelegate:self didEndSelector:@selector(sheetDidEnd:returnCode:contextInfo:) contextInfo:NULL];
  212. }
  213. - (void)sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
  214. {
  215. NSURLCredential *credential = nil;
  216. NSURLAuthenticationChallenge *chall;
  217. ASSERT(usingSheet);
  218. ASSERT(challenge != nil);
  219. if (returnCode == 0) {
  220. credential = [[NSURLCredential alloc] initWithUser:[username stringValue] password:[password stringValue] persistence:([remember state] == NSOnState) ? NSURLCredentialPersistencePermanent : NSURLCredentialPersistenceForSession];
  221. }
  222. // We take this tricky approach to nilling out and releasing the challenge
  223. // because the callback below might remove our last ref.
  224. chall = challenge;
  225. challenge = nil;
  226. [callback performSelector:selector withObject:chall withObject:credential];
  227. [credential release];
  228. [chall release];
  229. }
  230. @end
  231. @implementation WebNonBlockingPanel
  232. - (BOOL)_blocksActionWhenModal:(SEL)theAction
  233. {
  234. // This override of a private AppKit method allows the user to quit when a login dialog
  235. // is onscreen, which is nice in general but in particular prevents pathological cases
  236. // like 3744583 from requiring a Force Quit.
  237. //
  238. // It would be nice to allow closing the individual window as well as quitting the app when
  239. // a login sheet is up, but this _blocksActionWhenModal: mechanism doesn't support that.
  240. // This override matches those in NSOpenPanel and NSToolbarConfigPanel.
  241. if (theAction == @selector(terminate:)) {
  242. return NO;
  243. }
  244. return YES;
  245. }
  246. @end