ASIAuthenticationDialog.m 17 KB

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