Plugins/Purple Service/AIPurpleOAuthJabberAccount.m
branchGTalkOAuth2Support
changeset 5089 a761745f82a1
equal deleted inserted replaced
5086:816c7d80c0e5 5089:a761745f82a1
       
     1 //
       
     2 //  AIPurpleOAuthJabberAccount.m
       
     3 //  Adium
       
     4 //
       
     5 //  Created by Thijs Alkemade on 18-09-12.
       
     6 //  Copyright (c) 2012 The Adium Team. All rights reserved.
       
     7 //
       
     8 
       
     9 #import "AIPurpleOAuthJabberAccount.h"
       
    10 #import <Adium/AIAccountControllerProtocol.h>
       
    11 
       
    12 #import "JSONKit.h"
       
    13 
       
    14 @implementation AIPurpleOAuthJabberAccount
       
    15 
       
    16 @synthesize oAuthWC;
       
    17 @synthesize oAuthToken;
       
    18 @synthesize networkState, connection, connectionResponse, connectionData;
       
    19 
       
    20 + (BOOL)uidIsValid:(NSString *)inUID
       
    21 {
       
    22 	return YES;
       
    23 }
       
    24 
       
    25 - (void)dealloc
       
    26 {
       
    27     [oAuthWC release];
       
    28     [oAuthToken release];
       
    29     
       
    30     [connection cancel];
       
    31     [connectionResponse release];
       
    32     [connectionData release];
       
    33 	
       
    34     [super dealloc];
       
    35 }
       
    36 
       
    37 #pragma mark Account configuration
       
    38 
       
    39 - (void)setName:(NSString *)name UID:(NSString *)inUID
       
    40 {
       
    41 	[self filterAndSetUID:inUID];
       
    42 	
       
    43 	[self setFormattedUID:name notify:NotifyNever];
       
    44 }
       
    45 
       
    46 /*!
       
    47  * @brief Password entered callback
       
    48  *
       
    49  * Callback after the user enters her password for connecting; finish the connect process.
       
    50  */
       
    51 - (void)passwordReturnedForConnect:(NSString *)inPassword returnCode:(AIPasswordPromptReturn)returnCode context:(id)inContext
       
    52 {
       
    53     if ((returnCode == AIPasswordPromptOKReturn) && (inPassword.length == 0)) {
       
    54 		/* No password retrieved from the keychain */
       
    55 		[self requestAuthorization];
       
    56 		
       
    57 	} else {
       
    58 		[self setValue:nil
       
    59 		   forProperty:@"mustPromptForPasswordOnNextConnect"
       
    60 				notify:NotifyNever];
       
    61 		[super passwordReturnedForConnect:inPassword returnCode:returnCode context:inContext];
       
    62 	}
       
    63 }
       
    64 
       
    65 
       
    66 - (void)retrievePasswordThenConnect
       
    67 {
       
    68 	if ([self boolValueForProperty:@"Prompt For Password On Next Connect"] ||
       
    69 		[self boolValueForProperty:@"mustPromptForPasswordOnNextConnect"])
       
    70 	/* We attempted to connect, but we had incorrect authorization. Display our auth request window. */
       
    71 		[self requestAuthorization];
       
    72 	
       
    73 	else {
       
    74 		/* Retrieve the user's password. Never prompt for a password, as we'll implement our own authorization handling
       
    75 		 * if the password can't be retrieved.
       
    76 		 */
       
    77 		[adium.accountController passwordForAccount:self
       
    78 									   promptOption:AIPromptNever
       
    79 									notifyingTarget:self
       
    80 										   selector:@selector(passwordReturnedForConnect:returnCode:context:)
       
    81 											context:nil];
       
    82 	}
       
    83 }
       
    84 
       
    85 
       
    86 - (void)requestAuthorization
       
    87 {
       
    88 	self.oAuthWC = [[[AIXMPPOAuthWebViewWindowController alloc] init] autorelease];
       
    89 	self.oAuthWC.account = self;
       
    90 	
       
    91 	[[NSNotificationCenter defaultCenter] postNotificationName:AIXMPPAuthProgressNotification
       
    92 														object:self
       
    93 													  userInfo:
       
    94 	 [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:AIXMPPOAuthProgressPromptingUser]
       
    95 								 forKey:KEY_XMPP_OAUTH_STEP]];
       
    96 	
       
    97 	[self.oAuthWC showWindow:self];
       
    98 }
       
    99 
       
   100 
       
   101 
       
   102 - (void)oAuthWebViewController:(AIXMPPOAuthWebViewWindowController *)wc didSucceedWithToken:(NSString *)token
       
   103 {
       
   104     [self setOAuthToken:token];
       
   105     
       
   106     NSString *urlstring = [self graphURLForToken:[self oAuthToken]];
       
   107     NSURL *url = [NSURL URLWithString:[urlstring stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
       
   108     NSURLRequest *request = [NSURLRequest requestWithURL:url];
       
   109     
       
   110     self.networkState = AIMeGraphAPINetworkState;
       
   111     self.connectionData = [NSMutableData data];
       
   112     self.connection = [NSURLConnection connectionWithRequest:request delegate:self];
       
   113 	
       
   114 	[[NSNotificationCenter defaultCenter] postNotificationName:AIXMPPAuthProgressNotification
       
   115 														object:self
       
   116 													  userInfo:
       
   117 	 [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:AIXMPPOAuthProgressContactingServer]
       
   118 								 forKey:KEY_XMPP_OAUTH_STEP]];
       
   119 }
       
   120 
       
   121 - (void)oAuthWebViewControllerDidFail:(AIXMPPOAuthWebViewWindowController *)wc
       
   122 {
       
   123 	[self setOAuthToken:nil];
       
   124 	
       
   125 	[[NSNotificationCenter defaultCenter] postNotificationName:AIXMPPAuthProgressNotification
       
   126 														object:self
       
   127 													  userInfo:
       
   128 	 [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:AIXMPPOAuthProgressFailure]
       
   129 								 forKey:KEY_XMPP_OAUTH_STEP]];
       
   130 	
       
   131 }
       
   132 
       
   133 - (void)meGraphAPIDidFinishLoading:(NSData *)graphAPIData response:(NSURLResponse *)inResponse error:(NSError *)inError
       
   134 {
       
   135     if (inError) {
       
   136         NSLog(@"error loading graph API: %@", inError);
       
   137         // TODO: indicate setup failed
       
   138         return;
       
   139     }
       
   140     
       
   141     NSError *error = nil;
       
   142     NSDictionary *resp = [graphAPIData objectFromJSONDataWithParseOptions:JKParseOptionNone error:&error];
       
   143     if (!resp) {
       
   144         NSLog(@"error decoding graph API response: %@", error);
       
   145         // TODO: indicate setup failed
       
   146         return;
       
   147     }
       
   148     
       
   149     NSString *uuid = [resp objectForKey:@"id"];
       
   150     NSString *name = [resp objectForKey:@"name"];
       
   151     
       
   152     /* Passwords are keyed by UID, so we need to make this change before storing the password */
       
   153 	[self setName:name UID:uuid];
       
   154 	
       
   155     NSString *secretURLString = [self promoteURLForToken:[self oAuthToken]];
       
   156     NSURL *secretURL = [NSURL URLWithString:[secretURLString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
       
   157     NSURLRequest *secretRequest = [NSURLRequest requestWithURL:secretURL];
       
   158 	
       
   159     self.networkState = AIPromoteSessionNetworkState;
       
   160     self.connectionData = [NSMutableData data];
       
   161     self.connection = [NSURLConnection connectionWithRequest:secretRequest delegate:self];
       
   162 	
       
   163 	[[NSNotificationCenter defaultCenter] postNotificationName:AIXMPPAuthProgressNotification
       
   164 														object:self
       
   165 													  userInfo:
       
   166 	 [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:AIXMPPOAuthProgressPromotingForChat]
       
   167 								 forKey:KEY_XMPP_OAUTH_STEP]];
       
   168 }
       
   169 
       
   170 - (void)didCompleteAuthorization
       
   171 {
       
   172 	/* Restart the connect process; we're currently considered 'connecting', so passwordReturnedForConnect:::
       
   173 	 * isn't going to restart it for us. */
       
   174 	[self connect];
       
   175 	
       
   176 	[[NSNotificationCenter defaultCenter] postNotificationName:AIXMPPAuthProgressNotification
       
   177 														object:self
       
   178 													  userInfo:
       
   179 	 [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:AIXMPPOAuthProgressSuccess]
       
   180 								 forKey:KEY_XMPP_OAUTH_STEP]];
       
   181 }
       
   182 
       
   183 - (void)promoteSessionDidFinishLoading:(NSData *)secretData response:(NSURLResponse *)response error:(NSError *)inError
       
   184 {
       
   185     if (inError) {
       
   186         NSLog(@"error promoting session: %@", inError);
       
   187         // TODO: indicate setup failed
       
   188         return;
       
   189     }
       
   190 	
       
   191 	NSString *sessionKey = [self oAuthToken];
       
   192 	[[adium accountController] setPassword:sessionKey forAccount:self];
       
   193 	
       
   194 	/* When we're newly authorized, connect! */
       
   195 	[self passwordReturnedForConnect:sessionKey
       
   196 						  returnCode:AIPasswordPromptOKReturn
       
   197 							 context:nil];
       
   198 	
       
   199 	[self didCompleteAuthorization];
       
   200 	
       
   201 	self.oAuthWC = nil;
       
   202     self.oAuthToken = nil;
       
   203 }
       
   204 
       
   205 #pragma mark NSURLConnectionDelegate
       
   206 
       
   207 - (void)connection:(NSURLConnection *)inConnection didReceiveResponse:(NSURLResponse *)response
       
   208 {
       
   209     [[self connectionData] setLength:0];
       
   210     [self setConnectionResponse:response];
       
   211 }
       
   212 
       
   213 - (void)connection:(NSURLConnection *)inConnection didReceiveData:(NSData *)data
       
   214 {
       
   215     [[self connectionData] appendData:data];
       
   216 }
       
   217 
       
   218 - (void)connection:(NSURLConnection *)inConnection didFailWithError:(NSError *)error
       
   219 {
       
   220     NSUInteger state = [self networkState];
       
   221     
       
   222     [self setNetworkState:AINoNetworkState];
       
   223     [self setConnection:nil];
       
   224     [self setConnectionResponse:nil];
       
   225     [self setConnectionData:nil];
       
   226     
       
   227     if (state == AIMeGraphAPINetworkState) {
       
   228         [self meGraphAPIDidFinishLoading:nil response:nil error:error];
       
   229     } else if (state == AIPromoteSessionNetworkState) {
       
   230         [self promoteSessionDidFinishLoading:nil response:nil error:error];
       
   231     }
       
   232 }
       
   233 
       
   234 - (void)connectionDidFinishLoading:(NSURLConnection *)inConnection
       
   235 {
       
   236     NSURLResponse *response = [[[self connectionResponse] retain] autorelease];
       
   237     NSMutableData *data = [[[self connectionData] retain] autorelease];
       
   238     NSUInteger state = [self networkState];
       
   239     
       
   240     [self setNetworkState:AINoNetworkState];
       
   241     [self setConnection:nil];
       
   242     [self setConnectionResponse:nil];
       
   243     [self setConnectionData:nil];
       
   244     
       
   245     if (state == AIMeGraphAPINetworkState) {
       
   246         [self meGraphAPIDidFinishLoading:data response:response error:nil];
       
   247     } else if (state == AIPromoteSessionNetworkState) {
       
   248         [self promoteSessionDidFinishLoading:data response:response error:nil];
       
   249     }
       
   250 }
       
   251 
       
   252 @end