root / asi-http-request-with-pithos / Classes / ASIAuthenticationDialog.m @ be116d22
History | View | Annotate | Download (15.5 kB)
1 |
// |
---|---|
2 |
// ASIAuthenticationDialog.m |
3 |
// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest |
4 |
// |
5 |
// Created by Ben Copsey on 21/08/2009. |
6 |
// Copyright 2009 All-Seeing Interactive. All rights reserved. |
7 |
// |
8 |
|
9 |
#import "ASIAuthenticationDialog.h" |
10 |
#import "ASIHTTPRequest.h" |
11 |
#import <QuartzCore/QuartzCore.h> |
12 |
|
13 |
static ASIAuthenticationDialog *sharedDialog = nil; |
14 |
BOOL isDismissing = NO; |
15 |
static NSMutableArray *requestsNeedingAuthentication = nil; |
16 |
|
17 |
static const NSUInteger kUsernameRow = 0; |
18 |
static const NSUInteger kUsernameSection = 0; |
19 |
static const NSUInteger kPasswordRow = 1; |
20 |
static const NSUInteger kPasswordSection = 0; |
21 |
static const NSUInteger kDomainRow = 0; |
22 |
static const NSUInteger kDomainSection = 1; |
23 |
|
24 |
|
25 |
@implementation ASIAutorotatingViewController |
26 |
|
27 |
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation |
28 |
{ |
29 |
return YES; |
30 |
} |
31 |
|
32 |
@end |
33 |
|
34 |
|
35 |
@interface ASIAuthenticationDialog () |
36 |
- (void)showTitle; |
37 |
- (void)show; |
38 |
- (NSArray *)requestsRequiringTheseCredentials; |
39 |
- (void)presentNextDialog; |
40 |
- (void)keyboardWillShow:(NSNotification *)notification; |
41 |
- (void)orientationChanged:(NSNotification *)notification; |
42 |
- (void)cancelAuthenticationFromDialog:(id)sender; |
43 |
- (void)loginWithCredentialsFromDialog:(id)sender; |
44 |
@property (retain) UITableView *tableView; |
45 |
@end |
46 |
|
47 |
@implementation ASIAuthenticationDialog |
48 |
|
49 |
#pragma mark init / dealloc |
50 |
|
51 |
+ (void)initialize |
52 |
{ |
53 |
if (self == [ASIAuthenticationDialog class]) { |
54 |
requestsNeedingAuthentication = [[NSMutableArray array] retain]; |
55 |
} |
56 |
} |
57 |
|
58 |
+ (void)presentAuthenticationDialogForRequest:(ASIHTTPRequest *)theRequest |
59 |
{ |
60 |
// No need for a lock here, this will always be called on the main thread |
61 |
if (!sharedDialog) { |
62 |
sharedDialog = [[self alloc] init]; |
63 |
[sharedDialog setRequest:theRequest]; |
64 |
if ([theRequest authenticationNeeded] == ASIProxyAuthenticationNeeded) { |
65 |
[sharedDialog setType:ASIProxyAuthenticationType]; |
66 |
} else { |
67 |
[sharedDialog setType:ASIStandardAuthenticationType]; |
68 |
} |
69 |
[sharedDialog show]; |
70 |
} else { |
71 |
[requestsNeedingAuthentication addObject:theRequest]; |
72 |
} |
73 |
} |
74 |
|
75 |
- (id)init |
76 |
{ |
77 |
if ((self = [self initWithNibName:nil bundle:nil])) { |
78 |
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil]; |
79 |
|
80 |
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_3_2 |
81 |
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) { |
82 |
#endif |
83 |
if (![UIDevice currentDevice].generatesDeviceOrientationNotifications) { |
84 |
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; |
85 |
[self setDidEnableRotationNotifications:YES]; |
86 |
} |
87 |
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:nil]; |
88 |
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_3_2 |
89 |
} |
90 |
#endif |
91 |
} |
92 |
return self; |
93 |
} |
94 |
|
95 |
- (void)dealloc |
96 |
{ |
97 |
if ([self didEnableRotationNotifications]) { |
98 |
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil]; |
99 |
} |
100 |
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; |
101 |
|
102 |
[request release]; |
103 |
[tableView release]; |
104 |
[presentingController.view removeFromSuperview]; |
105 |
[presentingController release]; |
106 |
[super dealloc]; |
107 |
} |
108 |
|
109 |
#pragma mark keyboard notifications |
110 |
|
111 |
- (void)keyboardWillShow:(NSNotification *)notification |
112 |
{ |
113 |
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_3_2 |
114 |
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) { |
115 |
#endif |
116 |
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_3_2 |
117 |
NSValue *keyboardBoundsValue = [[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey]; |
118 |
#else |
119 |
NSValue *keyboardBoundsValue = [[notification userInfo] objectForKey:UIKeyboardBoundsUserInfoKey]; |
120 |
#endif |
121 |
CGRect keyboardBounds; |
122 |
[keyboardBoundsValue getValue:&keyboardBounds]; |
123 |
UIEdgeInsets e = UIEdgeInsetsMake(0, 0, keyboardBounds.size.height, 0); |
124 |
[[self tableView] setScrollIndicatorInsets:e]; |
125 |
[[self tableView] setContentInset:e]; |
126 |
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_3_2 |
127 |
} |
128 |
#endif |
129 |
} |
130 |
|
131 |
// Manually handles orientation changes on iPhone |
132 |
- (void)orientationChanged:(NSNotification *)notification |
133 |
{ |
134 |
[self showTitle]; |
135 |
|
136 |
UIInterfaceOrientation o = [[UIApplication sharedApplication] statusBarOrientation]; |
137 |
CGFloat angle = 0; |
138 |
switch (o) { |
139 |
case UIDeviceOrientationLandscapeLeft: angle = 90; break; |
140 |
case UIDeviceOrientationLandscapeRight: angle = -90; break; |
141 |
case UIDeviceOrientationPortraitUpsideDown: angle = 180; break; |
142 |
default: break; |
143 |
} |
144 |
|
145 |
CGRect f = [[UIScreen mainScreen] applicationFrame]; |
146 |
|
147 |
// Swap the frame height and width if necessary |
148 |
if (UIDeviceOrientationIsLandscape(o)) { |
149 |
CGFloat t; |
150 |
t = f.size.width; |
151 |
f.size.width = f.size.height; |
152 |
f.size.height = t; |
153 |
} |
154 |
|
155 |
CGAffineTransform previousTransform = self.view.layer.affineTransform; |
156 |
CGAffineTransform newTransform = CGAffineTransformMakeRotation((CGFloat)(angle * M_PI / 180.0)); |
157 |
|
158 |
// Reset the transform so we can set the size |
159 |
self.view.layer.affineTransform = CGAffineTransformIdentity; |
160 |
self.view.frame = (CGRect){ { 0, 0 }, f.size}; |
161 |
|
162 |
// Revert to the previous transform for correct animation |
163 |
self.view.layer.affineTransform = previousTransform; |
164 |
|
165 |
[UIView beginAnimations:nil context:NULL]; |
166 |
[UIView setAnimationDuration:0.3]; |
167 |
|
168 |
// Set the new transform |
169 |
self.view.layer.affineTransform = newTransform; |
170 |
|
171 |
// Fix the view origin |
172 |
self.view.frame = (CGRect){ { f.origin.x, f.origin.y },self.view.frame.size}; |
173 |
[UIView commitAnimations]; |
174 |
} |
175 |
|
176 |
#pragma mark utilities |
177 |
|
178 |
- (UIViewController *)presentingController |
179 |
{ |
180 |
if (!presentingController) { |
181 |
presentingController = [[ASIAutorotatingViewController alloc] initWithNibName:nil bundle:nil]; |
182 |
|
183 |
// Attach to the window, but don't interfere. |
184 |
UIWindow *window = [[[UIApplication sharedApplication] windows] objectAtIndex:0]; |
185 |
[window addSubview:[presentingController view]]; |
186 |
[[presentingController view] setFrame:CGRectZero]; |
187 |
[[presentingController view] setUserInteractionEnabled:NO]; |
188 |
} |
189 |
|
190 |
return presentingController; |
191 |
} |
192 |
|
193 |
- (UITextField *)textFieldInRow:(NSUInteger)row section:(NSUInteger)section |
194 |
{ |
195 |
return [[[[[self tableView] cellForRowAtIndexPath: |
196 |
[NSIndexPath indexPathForRow:row inSection:section]] |
197 |
contentView] subviews] objectAtIndex:0]; |
198 |
} |
199 |
|
200 |
- (UITextField *)usernameField |
201 |
{ |
202 |
return [self textFieldInRow:kUsernameRow section:kUsernameSection]; |
203 |
} |
204 |
|
205 |
- (UITextField *)passwordField |
206 |
{ |
207 |
return [self textFieldInRow:kPasswordRow section:kPasswordSection]; |
208 |
} |
209 |
|
210 |
- (UITextField *)domainField |
211 |
{ |
212 |
return [self textFieldInRow:kDomainRow section:kDomainSection]; |
213 |
} |
214 |
|
215 |
#pragma mark show / dismiss |
216 |
|
217 |
+ (void)dismiss |
218 |
{ |
219 |
[[sharedDialog parentViewController] dismissModalViewControllerAnimated:YES]; |
220 |
} |
221 |
|
222 |
- (void)viewDidDisappear:(BOOL)animated |
223 |
{ |
224 |
[self retain]; |
225 |
[sharedDialog release]; |
226 |
sharedDialog = nil; |
227 |
[self performSelector:@selector(presentNextDialog) withObject:nil afterDelay:0]; |
228 |
[self release]; |
229 |
} |
230 |
|
231 |
- (void)dismiss |
232 |
{ |
233 |
if (self == sharedDialog) { |
234 |
[[self class] dismiss]; |
235 |
} else { |
236 |
[[self parentViewController] dismissModalViewControllerAnimated:YES]; |
237 |
} |
238 |
} |
239 |
|
240 |
- (void)showTitle |
241 |
{ |
242 |
UINavigationBar *navigationBar = [[[self view] subviews] objectAtIndex:0]; |
243 |
UINavigationItem *navItem = [[navigationBar items] objectAtIndex:0]; |
244 |
if (UIInterfaceOrientationIsPortrait([[UIDevice currentDevice] orientation])) { |
245 |
// Setup the title |
246 |
if ([self type] == ASIProxyAuthenticationType) { |
247 |
[navItem setPrompt:@"Login to this secure proxy server."]; |
248 |
} else { |
249 |
[navItem setPrompt:@"Login to this secure server."]; |
250 |
} |
251 |
} else { |
252 |
[navItem setPrompt:nil]; |
253 |
} |
254 |
[navigationBar sizeToFit]; |
255 |
CGRect f = [[self view] bounds]; |
256 |
f.origin.y = [navigationBar frame].size.height; |
257 |
f.size.height -= f.origin.y; |
258 |
[[self tableView] setFrame:f]; |
259 |
} |
260 |
|
261 |
- (void)show |
262 |
{ |
263 |
// Remove all subviews |
264 |
UIView *v; |
265 |
while ((v = [[[self view] subviews] lastObject])) { |
266 |
[v removeFromSuperview]; |
267 |
} |
268 |
|
269 |
// Setup toolbar |
270 |
UINavigationBar *bar = [[[UINavigationBar alloc] init] autorelease]; |
271 |
[bar setAutoresizingMask:UIViewAutoresizingFlexibleWidth]; |
272 |
|
273 |
UINavigationItem *navItem = [[[UINavigationItem alloc] init] autorelease]; |
274 |
bar.items = [NSArray arrayWithObject:navItem]; |
275 |
|
276 |
[[self view] addSubview:bar]; |
277 |
|
278 |
[self showTitle]; |
279 |
|
280 |
// Setup toolbar buttons |
281 |
if ([self type] == ASIProxyAuthenticationType) { |
282 |
[navItem setTitle:[[self request] proxyHost]]; |
283 |
} else { |
284 |
[navItem setTitle:[[[self request] url] host]]; |
285 |
} |
286 |
|
287 |
[navItem setLeftBarButtonItem:[[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancelAuthenticationFromDialog:)] autorelease]]; |
288 |
[navItem setRightBarButtonItem:[[[UIBarButtonItem alloc] initWithTitle:@"Login" style:UIBarButtonItemStyleDone target:self action:@selector(loginWithCredentialsFromDialog:)] autorelease]]; |
289 |
|
290 |
// We show the login form in a table view, similar to Safari's authentication dialog |
291 |
[bar sizeToFit]; |
292 |
CGRect f = [[self view] bounds]; |
293 |
f.origin.y = [bar frame].size.height; |
294 |
f.size.height -= f.origin.y; |
295 |
|
296 |
[self setTableView:[[[UITableView alloc] initWithFrame:f style:UITableViewStyleGrouped] autorelease]]; |
297 |
[[self tableView] setDelegate:self]; |
298 |
[[self tableView] setDataSource:self]; |
299 |
[[self tableView] setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight]; |
300 |
[[self view] addSubview:[self tableView]]; |
301 |
|
302 |
// Force reload the table content, and focus the first field to show the keyboard |
303 |
[[self tableView] reloadData]; |
304 |
[[[[[self tableView] cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]].contentView subviews] objectAtIndex:0] becomeFirstResponder]; |
305 |
|
306 |
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_3_2 |
307 |
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { |
308 |
[self setModalPresentationStyle:UIModalPresentationFormSheet]; |
309 |
} |
310 |
#endif |
311 |
|
312 |
[[self presentingController] presentModalViewController:self animated:YES]; |
313 |
} |
314 |
|
315 |
#pragma mark button callbacks |
316 |
|
317 |
- (void)cancelAuthenticationFromDialog:(id)sender |
318 |
{ |
319 |
for (ASIHTTPRequest *theRequest in [self requestsRequiringTheseCredentials]) { |
320 |
[theRequest cancelAuthentication]; |
321 |
[requestsNeedingAuthentication removeObject:theRequest]; |
322 |
} |
323 |
[self dismiss]; |
324 |
} |
325 |
|
326 |
- (NSArray *)requestsRequiringTheseCredentials |
327 |
{ |
328 |
NSMutableArray *requestsRequiringTheseCredentials = [NSMutableArray array]; |
329 |
NSURL *requestURL = [[self request] url]; |
330 |
for (ASIHTTPRequest *otherRequest in requestsNeedingAuthentication) { |
331 |
NSURL *theURL = [otherRequest url]; |
332 |
if (([otherRequest authenticationNeeded] == [[self request] authenticationNeeded]) && [[theURL host] isEqualToString:[requestURL host]] && ([theURL port] == [requestURL port] || ([requestURL port] && [[theURL port] isEqualToNumber:[requestURL port]])) && [[theURL scheme] isEqualToString:[requestURL scheme]] && ((![otherRequest authenticationRealm] && ![[self request] authenticationRealm]) || ([otherRequest authenticationRealm] && [[self request] authenticationRealm] && [[[self request] authenticationRealm] isEqualToString:[otherRequest authenticationRealm]]))) { |
333 |
[requestsRequiringTheseCredentials addObject:otherRequest]; |
334 |
} |
335 |
} |
336 |
[requestsRequiringTheseCredentials addObject:[self request]]; |
337 |
return requestsRequiringTheseCredentials; |
338 |
} |
339 |
|
340 |
- (void)presentNextDialog |
341 |
{ |
342 |
if ([requestsNeedingAuthentication count]) { |
343 |
ASIHTTPRequest *nextRequest = [requestsNeedingAuthentication objectAtIndex:0]; |
344 |
[requestsNeedingAuthentication removeObjectAtIndex:0]; |
345 |
[[self class] presentAuthenticationDialogForRequest:nextRequest]; |
346 |
} |
347 |
} |
348 |
|
349 |
|
350 |
- (void)loginWithCredentialsFromDialog:(id)sender |
351 |
{ |
352 |
for (ASIHTTPRequest *theRequest in [self requestsRequiringTheseCredentials]) { |
353 |
|
354 |
NSString *username = [[self usernameField] text]; |
355 |
NSString *password = [[self passwordField] text]; |
356 |
|
357 |
if (username == nil) { username = @""; } |
358 |
if (password == nil) { password = @""; } |
359 |
|
360 |
if ([self type] == ASIProxyAuthenticationType) { |
361 |
[theRequest setProxyUsername:username]; |
362 |
[theRequest setProxyPassword:password]; |
363 |
} else { |
364 |
[theRequest setUsername:username]; |
365 |
[theRequest setPassword:password]; |
366 |
} |
367 |
|
368 |
// Handle NTLM domains |
369 |
NSString *scheme = ([self type] == ASIStandardAuthenticationType) ? [[self request] authenticationScheme] : [[self request] proxyAuthenticationScheme]; |
370 |
if ([scheme isEqualToString:(NSString *)kCFHTTPAuthenticationSchemeNTLM]) { |
371 |
NSString *domain = [[self domainField] text]; |
372 |
if ([self type] == ASIProxyAuthenticationType) { |
373 |
[theRequest setProxyDomain:domain]; |
374 |
} else { |
375 |
[theRequest setDomain:domain]; |
376 |
} |
377 |
} |
378 |
|
379 |
[theRequest retryUsingSuppliedCredentials]; |
380 |
[requestsNeedingAuthentication removeObject:theRequest]; |
381 |
} |
382 |
[self dismiss]; |
383 |
} |
384 |
|
385 |
#pragma mark table view data source |
386 |
|
387 |
- (NSInteger)numberOfSectionsInTableView:(UITableView *)aTableView |
388 |
{ |
389 |
NSString *scheme = ([self type] == ASIStandardAuthenticationType) ? [[self request] authenticationScheme] : [[self request] proxyAuthenticationScheme]; |
390 |
if ([scheme isEqualToString:(NSString *)kCFHTTPAuthenticationSchemeNTLM]) { |
391 |
return 2; |
392 |
} |
393 |
return 1; |
394 |
} |
395 |
|
396 |
- (CGFloat)tableView:(UITableView *)aTableView heightForFooterInSection:(NSInteger)section |
397 |
{ |
398 |
if (section == [self numberOfSectionsInTableView:aTableView]-1) { |
399 |
return 30; |
400 |
} |
401 |
return 0; |
402 |
} |
403 |
|
404 |
- (CGFloat)tableView:(UITableView *)aTableView heightForHeaderInSection:(NSInteger)section |
405 |
{ |
406 |
if (section == 0) { |
407 |
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_3_2 |
408 |
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { |
409 |
return 54; |
410 |
} |
411 |
#endif |
412 |
return 30; |
413 |
} |
414 |
return 0; |
415 |
} |
416 |
|
417 |
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section |
418 |
{ |
419 |
if (section == 0) { |
420 |
return [[self request] authenticationRealm]; |
421 |
} |
422 |
return nil; |
423 |
} |
424 |
|
425 |
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath |
426 |
{ |
427 |
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_3_0 |
428 |
UITableViewCell *cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil] autorelease]; |
429 |
#else |
430 |
UITableViewCell *cell = [[[UITableViewCell alloc] initWithFrame:CGRectMake(0,0,0,0) reuseIdentifier:nil] autorelease]; |
431 |
#endif |
432 |
|
433 |
[cell setSelectionStyle:UITableViewCellSelectionStyleNone]; |
434 |
|
435 |
CGRect f = CGRectInset([cell bounds], 10, 10); |
436 |
UITextField *textField = [[[UITextField alloc] initWithFrame:f] autorelease]; |
437 |
[textField setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight]; |
438 |
[textField setAutocapitalizationType:UITextAutocapitalizationTypeNone]; |
439 |
[textField setAutocorrectionType:UITextAutocorrectionTypeNo]; |
440 |
|
441 |
NSUInteger s = [indexPath section]; |
442 |
NSUInteger r = [indexPath row]; |
443 |
|
444 |
if (s == kUsernameSection && r == kUsernameRow) { |
445 |
[textField setPlaceholder:@"User"]; |
446 |
} else if (s == kPasswordSection && r == kPasswordRow) { |
447 |
[textField setPlaceholder:@"Password"]; |
448 |
[textField setSecureTextEntry:YES]; |
449 |
} else if (s == kDomainSection && r == kDomainRow) { |
450 |
[textField setPlaceholder:@"Domain"]; |
451 |
} |
452 |
[cell.contentView addSubview:textField]; |
453 |
|
454 |
return cell; |
455 |
} |
456 |
|
457 |
- (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section |
458 |
{ |
459 |
if (section == 0) { |
460 |
return 2; |
461 |
} else { |
462 |
return 1; |
463 |
} |
464 |
} |
465 |
|
466 |
- (NSString *)tableView:(UITableView *)aTableView titleForFooterInSection:(NSInteger)section |
467 |
{ |
468 |
if (section == [self numberOfSectionsInTableView:aTableView]-1) { |
469 |
// If we're using Basic authentication and the connection is not using SSL, we'll show the plain text message |
470 |
if ([[[self request] authenticationScheme] isEqualToString:(NSString *)kCFHTTPAuthenticationSchemeBasic] && ![[[[self request] url] scheme] isEqualToString:@"https"]) { |
471 |
return @"Password will be sent in the clear."; |
472 |
// We are using Digest, NTLM, or any scheme over SSL |
473 |
} else { |
474 |
return @"Password will be sent securely."; |
475 |
} |
476 |
} |
477 |
return nil; |
478 |
} |
479 |
|
480 |
#pragma mark - |
481 |
|
482 |
@synthesize request; |
483 |
@synthesize type; |
484 |
@synthesize tableView; |
485 |
@synthesize didEnableRotationNotifications; |
486 |
@synthesize presentingController; |
487 |
@end |