Statistics
| Branch: | Revision:

root / asi-http-request-with-pithos / Classes / ASIAuthenticationDialog.m @ cc176feb

History | View | Annotate | Download (15.8 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 = (UIInterfaceOrientation)[[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
	if ([sharedDialog respondsToSelector:@selector(presentingViewController)])
220
		[[sharedDialog presentingViewController] dismissModalViewControllerAnimated:YES];
221
	else 
222
		[[sharedDialog parentViewController] dismissModalViewControllerAnimated:YES];
223
}
224

    
225
- (void)viewDidDisappear:(BOOL)animated
226
{
227
	[self retain];
228
	[sharedDialog release];
229
	sharedDialog = nil;
230
	[self performSelector:@selector(presentNextDialog) withObject:nil afterDelay:0];
231
	[self release];
232
}
233

    
234
- (void)dismiss
235
{
236
	if (self == sharedDialog) {
237
		[[self class] dismiss];
238
	} else {
239
		if ([self respondsToSelector:@selector(presentingViewController)])
240
			[[self presentingViewController] dismissModalViewControllerAnimated:YES];
241
		else
242
			[[self parentViewController] dismissModalViewControllerAnimated:YES];
243
	}
244
}
245

    
246
- (void)showTitle
247
{
248
	UINavigationBar *navigationBar = [[[self view] subviews] objectAtIndex:0];
249
	UINavigationItem *navItem = [[navigationBar items] objectAtIndex:0];
250
	if (UIInterfaceOrientationIsPortrait([[UIDevice currentDevice] orientation])) {
251
		// Setup the title
252
		if ([self type] == ASIProxyAuthenticationType) {
253
			[navItem setPrompt:@"Login to this secure proxy server."];
254
		} else {
255
			[navItem setPrompt:@"Login to this secure server."];
256
		}
257
	} else {
258
		[navItem setPrompt:nil];
259
	}
260
	[navigationBar sizeToFit];
261
	CGRect f = [[self view] bounds];
262
	f.origin.y = [navigationBar frame].size.height;
263
	f.size.height -= f.origin.y;
264
	[[self tableView] setFrame:f];
265
}
266

    
267
- (void)show
268
{
269
	// Remove all subviews
270
	UIView *v;
271
	while ((v = [[[self view] subviews] lastObject])) {
272
		[v removeFromSuperview];
273
	}
274

    
275
	// Setup toolbar
276
	UINavigationBar *bar = [[[UINavigationBar alloc] init] autorelease];
277
	[bar setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
278

    
279
	UINavigationItem *navItem = [[[UINavigationItem alloc] init] autorelease];
280
	bar.items = [NSArray arrayWithObject:navItem];
281

    
282
	[[self view] addSubview:bar];
283

    
284
	[self showTitle];
285

    
286
	// Setup toolbar buttons
287
	if ([self type] == ASIProxyAuthenticationType) {
288
		[navItem setTitle:[[self request] proxyHost]];
289
	} else {
290
		[navItem setTitle:[[[self request] url] host]];
291
	}
292

    
293
	[navItem setLeftBarButtonItem:[[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancelAuthenticationFromDialog:)] autorelease]];
294
	[navItem setRightBarButtonItem:[[[UIBarButtonItem alloc] initWithTitle:@"Login" style:UIBarButtonItemStyleDone target:self action:@selector(loginWithCredentialsFromDialog:)] autorelease]];
295

    
296
	// We show the login form in a table view, similar to Safari's authentication dialog
297
	[bar sizeToFit];
298
	CGRect f = [[self view] bounds];
299
	f.origin.y = [bar frame].size.height;
300
	f.size.height -= f.origin.y;
301

    
302
	[self setTableView:[[[UITableView alloc] initWithFrame:f style:UITableViewStyleGrouped] autorelease]];
303
	[[self tableView] setDelegate:self];
304
	[[self tableView] setDataSource:self];
305
	[[self tableView] setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
306
	[[self view] addSubview:[self tableView]];
307

    
308
	// Force reload the table content, and focus the first field to show the keyboard
309
	[[self tableView] reloadData];
310
	[[[[[self tableView] cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]].contentView subviews] objectAtIndex:0] becomeFirstResponder];
311

    
312
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_3_2
313
	if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
314
		[self setModalPresentationStyle:UIModalPresentationFormSheet];
315
	}
316
#endif
317

    
318
	[[self presentingController] presentModalViewController:self animated:YES];
319
}
320

    
321
#pragma mark button callbacks
322

    
323
- (void)cancelAuthenticationFromDialog:(id)sender
324
{
325
	for (ASIHTTPRequest *theRequest in [self requestsRequiringTheseCredentials]) {
326
		[theRequest cancelAuthentication];
327
		[requestsNeedingAuthentication removeObject:theRequest];
328
	}
329
	[self dismiss];
330
}
331

    
332
- (NSArray *)requestsRequiringTheseCredentials
333
{
334
	NSMutableArray *requestsRequiringTheseCredentials = [NSMutableArray array];
335
	NSURL *requestURL = [[self request] url];
336
	for (ASIHTTPRequest *otherRequest in requestsNeedingAuthentication) {
337
		NSURL *theURL = [otherRequest url];
338
		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]]))) {
339
			[requestsRequiringTheseCredentials addObject:otherRequest];
340
		}
341
	}
342
	[requestsRequiringTheseCredentials addObject:[self request]];
343
	return requestsRequiringTheseCredentials;
344
}
345

    
346
- (void)presentNextDialog
347
{
348
	if ([requestsNeedingAuthentication count]) {
349
		ASIHTTPRequest *nextRequest = [requestsNeedingAuthentication objectAtIndex:0];
350
		[requestsNeedingAuthentication removeObjectAtIndex:0];
351
		[[self class] presentAuthenticationDialogForRequest:nextRequest];
352
	}
353
}
354

    
355

    
356
- (void)loginWithCredentialsFromDialog:(id)sender
357
{
358
	for (ASIHTTPRequest *theRequest in [self requestsRequiringTheseCredentials]) {
359

    
360
		NSString *username = [[self usernameField] text];
361
		NSString *password = [[self passwordField] text];
362

    
363
		if (username == nil) { username = @""; }
364
		if (password == nil) { password = @""; }
365

    
366
		if ([self type] == ASIProxyAuthenticationType) {
367
			[theRequest setProxyUsername:username];
368
			[theRequest setProxyPassword:password];
369
		} else {
370
			[theRequest setUsername:username];
371
			[theRequest setPassword:password];
372
		}
373

    
374
		// Handle NTLM domains
375
		NSString *scheme = ([self type] == ASIStandardAuthenticationType) ? [[self request] authenticationScheme] : [[self request] proxyAuthenticationScheme];
376
		if ([scheme isEqualToString:(NSString *)kCFHTTPAuthenticationSchemeNTLM]) {
377
			NSString *domain = [[self domainField] text];
378
			if ([self type] == ASIProxyAuthenticationType) {
379
				[theRequest setProxyDomain:domain];
380
			} else {
381
				[theRequest setDomain:domain];
382
			}
383
		}
384

    
385
		[theRequest retryUsingSuppliedCredentials];
386
		[requestsNeedingAuthentication removeObject:theRequest];
387
	}
388
	[self dismiss];
389
}
390

    
391
#pragma mark table view data source
392

    
393
- (NSInteger)numberOfSectionsInTableView:(UITableView *)aTableView
394
{
395
	NSString *scheme = ([self type] == ASIStandardAuthenticationType) ? [[self request] authenticationScheme] : [[self request] proxyAuthenticationScheme];
396
	if ([scheme isEqualToString:(NSString *)kCFHTTPAuthenticationSchemeNTLM]) {
397
		return 2;
398
	}
399
	return 1;
400
}
401

    
402
- (CGFloat)tableView:(UITableView *)aTableView heightForFooterInSection:(NSInteger)section
403
{
404
	if (section == [self numberOfSectionsInTableView:aTableView]-1) {
405
		return 30;
406
	}
407
	return 0;
408
}
409

    
410
- (CGFloat)tableView:(UITableView *)aTableView heightForHeaderInSection:(NSInteger)section
411
{
412
	if (section == 0) {
413
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_3_2
414
		if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
415
			return 54;
416
		}
417
#endif
418
		return 30;
419
	}
420
	return 0;
421
}
422

    
423
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
424
{
425
	if (section == 0) {
426
		return [[self request] authenticationRealm];
427
	}
428
	return nil;
429
}
430

    
431
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
432
{
433
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_3_0
434
	UITableViewCell *cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil] autorelease];
435
#else
436
	UITableViewCell *cell = [[[UITableViewCell alloc] initWithFrame:CGRectMake(0,0,0,0) reuseIdentifier:nil] autorelease];
437
#endif
438

    
439
	[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
440

    
441
	CGRect f = CGRectInset([cell bounds], 10, 10);
442
	UITextField *textField = [[[UITextField alloc] initWithFrame:f] autorelease];
443
	[textField setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
444
	[textField setAutocapitalizationType:UITextAutocapitalizationTypeNone];
445
	[textField setAutocorrectionType:UITextAutocorrectionTypeNo];
446

    
447
	NSUInteger s = [indexPath section];
448
	NSUInteger r = [indexPath row];
449

    
450
	if (s == kUsernameSection && r == kUsernameRow) {
451
		[textField setPlaceholder:@"User"];
452
	} else if (s == kPasswordSection && r == kPasswordRow) {
453
		[textField setPlaceholder:@"Password"];
454
		[textField setSecureTextEntry:YES];
455
	} else if (s == kDomainSection && r == kDomainRow) {
456
		[textField setPlaceholder:@"Domain"];
457
	}
458
	[cell.contentView addSubview:textField];
459

    
460
	return cell;
461
}
462

    
463
- (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section
464
{
465
	if (section == 0) {
466
		return 2;
467
	} else {
468
		return 1;
469
	}
470
}
471

    
472
- (NSString *)tableView:(UITableView *)aTableView titleForFooterInSection:(NSInteger)section
473
{
474
	if (section == [self numberOfSectionsInTableView:aTableView]-1) {
475
		// If we're using Basic authentication and the connection is not using SSL, we'll show the plain text message
476
		if ([[[self request] authenticationScheme] isEqualToString:(NSString *)kCFHTTPAuthenticationSchemeBasic] && ![[[[self request] url] scheme] isEqualToString:@"https"]) {
477
			return @"Password will be sent in the clear.";
478
		// We are using Digest, NTLM, or any scheme over SSL
479
		} else {
480
			return @"Password will be sent securely.";
481
		}
482
	}
483
	return nil;
484
}
485

    
486
#pragma mark -
487

    
488
@synthesize request;
489
@synthesize type;
490
@synthesize tableView;
491
@synthesize didEnableRotationNotifications;
492
@synthesize presentingController;
493
@end