2
0

Bump External dependencies.

This commit is contained in:
Maarten Billemont
2013-08-11 00:08:25 -04:00
parent 77439af486
commit 8375808cdc
224 changed files with 13810 additions and 2949 deletions

View File

@@ -28,6 +28,7 @@
NSMutableDictionary *requestIDMap_;
BOOL skipAuthorization_;
NSDictionary *additionalHTTPHeaders_;
NSDictionary *urlQueryParameters_;
}
// Queries included in this batch. Each query should have a unique requestID.
@@ -42,6 +43,10 @@
// additionalHTTPHeaders.
@property (copy) NSDictionary *additionalHTTPHeaders;
// Any URL query parameters to add to the query (useful for debugging with some
// services).
@property (copy) NSDictionary *urlQueryParameters;
+ (id)batchQuery;
+ (id)batchQueryWithQueries:(NSArray *)array;

View File

@@ -22,7 +22,8 @@
@implementation GTLBatchQuery
@synthesize shouldSkipAuthorization = skipAuthorization_,
additionalHTTPHeaders = additionalHTTPHeaders_;
additionalHTTPHeaders = additionalHTTPHeaders_,
urlQueryParameters = urlQueryParameters_;
+ (id)batchQuery {
GTLBatchQuery *obj = [[[self alloc] init] autorelease];
@@ -49,6 +50,7 @@
- (void)dealloc {
[queries_ release];
[additionalHTTPHeaders_ release];
[urlQueryParameters_ release];
[requestIDMap_ release];
[super dealloc];

View File

@@ -28,6 +28,7 @@
- (BOOL)shouldSkipAuthorization;
- (void)executionDidStop;
- (NSDictionary *)additionalHTTPHeaders;
- (NSDictionary *)urlQueryParameters;
- (GTLUploadParameters *)uploadParameters;
@end
@@ -78,7 +79,7 @@
// or data must be provided.
@property (copy) GTLUploadParameters *uploadParameters;
// Any url query parameters to add to the query (useful for debugging with some
// Any URL query parameters to add to the query (useful for debugging with some
// services).
@property (copy) NSDictionary *urlQueryParameters;

View File

@@ -755,7 +755,7 @@ static NSString *ETagIfPresent(GTLObject *obj) {
// Build up the array of RPC calls.
NSMutableArray *rpcPayloads = [NSMutableArray arrayWithCapacity:numberOfQueries];
NSMutableSet *requestIDs = [NSMutableSet setWithCapacity:numberOfQueries];
NSMutableArray *requestIDs = [NSMutableSet setWithCapacity:numberOfQueries];
for (GTLQuery *query in queries) {
NSString *methodName = query.methodName;
NSDictionary *parameters = query.JSON;
@@ -772,6 +772,10 @@ static NSString *ETagIfPresent(GTLObject *obj) {
@"additionalHTTPHeaders disallowed on queries added to a batch - query %@ (%@)",
requestID, methodName);
GTL_DEBUG_ASSERT(query.urlQueryParameters == nil,
@"urlQueryParameters disallowed on queries added to a batch - query %@ (%@)",
requestID, methodName);
GTL_DEBUG_ASSERT(query.uploadParameters == nil,
@"uploadParameters disallowed on queries added to a batch - query %@ (%@)",
requestID, methodName);
@@ -802,10 +806,16 @@ static NSString *ETagIfPresent(GTLObject *obj) {
BOOL mayAuthorize = (batchCopy ? !batchCopy.shouldSkipAuthorization : YES);
// urlQueryParameters on the queries are currently unsupport during a batch
// as it's not clear how to map them.
NSURL *rpcURL = self.rpcURL;
// We'll use the batch query's URL parameters, and ignore the URL parameters
// specified on the individual queries.
NSDictionary *urlQueryParameters = batch.urlQueryParameters;
if ([urlQueryParameters count] > 0) {
rpcURL = [GTLUtilities URLWithString:[rpcURL absoluteString]
queryParameters:urlQueryParameters];
}
GTLServiceTicket *resultTicket = [self fetchObjectWithURL:rpcURL
objectClass:[GTLBatchResult class]
bodyObject:nil

View File

@@ -85,7 +85,9 @@
// iOS 6 and Mac OS X 10.7, clients may simply create an operation queue for
// callbacks on a background thread:
//
// fetcher.delegateQueue = [[[NSOperationQueue alloc] init] autorelease];
// NSOperationQueue *queue = [[[NSOperationQueue alloc] init] autorelease];
// [queue setMaxConcurrentOperationCount:1];
// fetcher.delegateQueue = queue;
//
// or specify the main queue for callbacks on the main thread:
//
@@ -383,7 +385,7 @@ NSString *GTMApplicationIdentifier(NSBundle *bundle);
@protocol GTMFetcherAuthorizationProtocol <NSObject>
@required
// This protocol allows us to call the authorizer without requiring its sources
// in this project
// in this project.
- (void)authorizeRequest:(NSMutableURLRequest *)request
delegate:(id)delegate
didFinishSelector:(SEL)sel;
@@ -396,12 +398,27 @@ NSString *GTMApplicationIdentifier(NSBundle *bundle);
- (BOOL)isAuthorizedRequest:(NSURLRequest *)request;
- (NSString *)userEmail;
@property (retain, readonly) NSString *userEmail;
@optional
// Indicate if authorization may be attempted. Even if this succeeds,
// authorization may fail if the user's permissions have been revoked.
@property (readonly) BOOL canAuthorize;
// For development only, allow authorization of non-SSL requests, allowing
// transmission of the bearer token unencrypted.
@property (assign) BOOL shouldAuthorizeAllRequests;
#if NS_BLOCKS_AVAILABLE
- (void)authorizeRequest:(NSMutableURLRequest *)request
completionHandler:(void (^)(NSError *error))handler;
#endif
@property (assign) id <GTMHTTPFetcherServiceProtocol> fetcherService; // WEAK
- (BOOL)primeForRefresh;
@end
// GTMHTTPFetcher objects are used for async retrieval of an http get or post

View File

@@ -142,6 +142,11 @@ static NSString *const kCallbackError = @"error";
// Default to system default cookie storage
[self setCookieStorageMethod:kGTMHTTPFetcherCookieStorageMethodSystemDefault];
}
#if !STRIP_GTM_FETCH_LOGGING
// Encourage developers to set the comment property or use
// setCommentWithFormat: by providing a default string.
comment_ = @"(No fetcher comment set)";
#endif
}
return self;
}
@@ -345,6 +350,13 @@ static NSString *const kCallbackError = @"error";
}
#endif
if (downloadFileHandle_ != nil) {
// Downloading to a file, so downloadedData_ remains nil.
} else {
self.downloadedData = [NSMutableData data];
}
hasConnectionEnded_ = NO;
if ([runLoopModes_ count] == 0 && delegateQueue == nil) {
// No custom callback modes or queue were specified, so start the connection
// on the current run loop in the current mode
@@ -367,19 +379,13 @@ static NSString *const kCallbackError = @"error";
}
[connection_ start];
}
hasConnectionEnded_ = NO;
if (!connection_) {
NSAssert(connection_ != nil, @"beginFetchWithDelegate could not create a connection");
self.downloadedData = nil;
goto CannotBeginFetch;
}
if (downloadFileHandle_ != nil) {
// downloading to a file, so downloadedData_ remains nil
} else {
self.downloadedData = [NSMutableData data];
}
#if GTM_BACKGROUND_FETCHING
backgroundTaskIdentifer_ = 0; // UIBackgroundTaskInvalid is 0 on iOS 4
if (shouldFetchInBackground_) {
@@ -388,15 +394,22 @@ static NSString *const kCallbackError = @"error";
if ([app respondsToSelector:@selector(beginBackgroundTaskWithExpirationHandler:)]) {
// Tell UIApplication that we want to continue even when the app is in the
// background.
NSThread *thread = [NSThread currentThread];
NSThread *thread = delegateQueue_ ? nil : [NSThread currentThread];
backgroundTaskIdentifer_ = [app beginBackgroundTaskWithExpirationHandler:^{
// Callback - this block is always invoked by UIApplication on the main
// thread, but we want to run the user's callbacks on the thread used
// to start the fetch.
[self performSelector:@selector(backgroundFetchExpired)
onThread:thread
withObject:nil
waitUntilDone:YES];
// Background task expiration callback - this block is always invoked by
// UIApplication on the main thread.
if (thread) {
// Run the user's callbacks on the thread used to start the
// fetch.
[self performSelector:@selector(backgroundFetchExpired)
onThread:thread
withObject:nil
waitUntilDone:YES];
} else {
// backgroundFetchExpired invokes callbacks on the provided delegate
// queue.
[self backgroundFetchExpired];
}
}];
}
}
@@ -673,7 +686,7 @@ CannotBeginFetch:
// this may be called in a callback from the connection, so use autorelease
[oldConnection autorelease];
}
}
} // @synchronized(self)
// send the stopped notification
[self sendStopNotificationIfNeeded];
@@ -692,7 +705,7 @@ CannotBeginFetch:
error:NULL];
self.temporaryDownloadPath = nil;
}
}
} // @synchronized(self)
[service fetcherDidStop:self];
@@ -824,7 +837,7 @@ CannotBeginFetch:
[self setMutableRequest:mutable];
}
return redirectRequest;
}
} // @synchronized(self)
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
@@ -900,7 +913,7 @@ didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
forAuthenticationChallenge:challenge];
return;
}
}
} // @synchronized(self)
// If we don't have credentials, or we've already failed auth 3x,
// report the error, putting the challenge as a value in the userInfo
@@ -1061,11 +1074,18 @@ totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite {
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
@synchronized(self) {
#if DEBUG
// The download file handle should be set before the fetch is started, not
// after
NSAssert(!hasConnectionEnded_, @"Connection received data after ending");
// The download file handle should be set or the data object allocated
// before the fetch is started.
NSAssert((downloadFileHandle_ == nil) != (downloadedData_ == nil),
@"received data accumulates as NSData or NSFileHandle, not both");
@"received data accumulates as either NSData (%d) or"
@" NSFileHandle (%d)",
(downloadedData_ != nil), (downloadFileHandle_ != nil));
#endif
// Hopefully, we'll never see this execute out-of-order, receiving data
// after we've received the finished or failed callback.
if (hasConnectionEnded_) return;
if (downloadFileHandle_ != nil) {
// Append to file
@@ -1102,7 +1122,7 @@ totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite {
receivedDataBlock_(downloadedData_);
}
#endif
}
} // @synchronized(self)
}
// For error 304's ("Not Modified") where we've cached the data, return
@@ -1228,7 +1248,7 @@ totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite {
#if !STRIP_GTM_FETCH_LOGGING
shouldDeferLogging = shouldDeferResponseBodyLogging_;
#endif
}
} // @synchronized(self)
if (shouldBeginRetryTimer) {
[self beginRetryTimer];
@@ -1470,7 +1490,7 @@ totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite {
retryTimer_ = nil;
shouldNotify = YES;
}
}
} // @synchronized(self)
if (shouldNotify) {
NSNotificationCenter *defaultNC = [NSNotificationCenter defaultCenter];

View File

@@ -80,6 +80,7 @@ _EXTERN NSString* const kGTMOAuth2FetchTypeUserInfo _INITIALIZE_AS(@"userInfo"
// Token-issuance errors
_EXTERN NSString* const kGTMOAuth2ErrorKey _INITIALIZE_AS(@"error");
_EXTERN NSString* const kGTMOAuth2ErrorObjectKey _INITIALIZE_AS(@"kGTMOAuth2ErrorObjectKey");
_EXTERN NSString* const kGTMOAuth2ErrorInvalidRequest _INITIALIZE_AS(@"invalid_request");
_EXTERN NSString* const kGTMOAuth2ErrorInvalidClient _INITIALIZE_AS(@"invalid_client");
@@ -93,8 +94,9 @@ _EXTERN NSString* const kGTMOAuth2ErrorInvalidScope _INITIALIZE_AS(@"inv
_EXTERN NSString* const kGTMOAuth2UserSignedIn _INITIALIZE_AS(@"kGTMOAuth2UserSignedIn");
// Notification for token changes
_EXTERN NSString* const kGTMOAuth2AccessTokenRefreshed _INITIALIZE_AS(@"kGTMOAuth2AccessTokenRefreshed");
_EXTERN NSString* const kGTMOAuth2RefreshTokenChanged _INITIALIZE_AS(@"kGTMOAuth2RefreshTokenChanged");
_EXTERN NSString* const kGTMOAuth2AccessTokenRefreshed _INITIALIZE_AS(@"kGTMOAuth2AccessTokenRefreshed");
_EXTERN NSString* const kGTMOAuth2RefreshTokenChanged _INITIALIZE_AS(@"kGTMOAuth2RefreshTokenChanged");
_EXTERN NSString* const kGTMOAuth2AccessTokenRefreshFailed _INITIALIZE_AS(@"kGTMOAuth2AccessTokenRefreshFailed");
// Notification for WebView loading
_EXTERN NSString* const kGTMOAuth2WebViewStartedLoading _INITIALIZE_AS(@"kGTMOAuth2WebViewStartedLoading");
@@ -120,7 +122,10 @@ _EXTERN NSString* const kGTMOAuth2NetworkFound _INITIALIZE_AS(@"kGTMOAuth
NSURL *tokenURL_;
NSDate *expirationDate_;
NSString *authorizationTokenKey_;
NSDictionary *additionalTokenRequestParameters_;
NSDictionary *additionalGrantTypeRequestParameters_;
// queue of requests for authorization waiting for a valid access token
GTMHTTPFetcher *refreshFetcher_;
@@ -152,9 +157,20 @@ _EXTERN NSString* const kGTMOAuth2NetworkFound _INITIALIZE_AS(@"kGTMOAuth
@property (retain) NSString *refreshScope;
// Apps may optionally add parameters here to be provided to the token
// endpoint on token requests and refreshes
// endpoint on token requests and refreshes.
@property (retain) NSDictionary *additionalTokenRequestParameters;
// Apps may optionally add parameters here to be provided to the token
// endpoint on specific token requests and refreshes, keyed by the grant_type.
// For example, if a different "type" parameter is required for obtaining
// the auth code and on refresh, this might be:
//
// viewController.authentication.additionalGrantTypeRequestParameters = @{
// @"authorization_code" : @{ @"type" : @"code" },
// @"refresh_token" : @{ @"type" : @"refresh" }
// };
@property (retain) NSDictionary *additionalGrantTypeRequestParameters;
// Response properties
@property (retain) NSMutableDictionary *parameters;
@@ -216,6 +232,11 @@ _EXTERN NSString* const kGTMOAuth2NetworkFound _INITIALIZE_AS(@"kGTMOAuth
// not set, the class SBJSON must be available in the runtime.
@property (assign) Class parserClass;
// Key for the response parameter used for the authorization header; by default,
// "access_token" is used, but some servers may expect alternatives, like
// "id_token".
@property (copy) NSString *authorizationTokenKey;
// Convenience method for creating an authentication object
+ (id)authenticationWithServiceProvider:(NSString *)serviceProvider
tokenURL:(NSURL *)tokenURL
@@ -327,6 +348,8 @@ _EXTERN NSString* const kGTMOAuth2NetworkFound _INITIALIZE_AS(@"kGTMOAuth
+ (NSDictionary *)dictionaryWithResponseString:(NSString *)responseStr;
+ (NSDictionary *)dictionaryWithJSONData:(NSData *)data;
+ (NSString *)scopeWithStrings:(NSString *)firsStr, ... NS_REQUIRES_NIL_TERMINATION;
@end

View File

@@ -122,6 +122,7 @@ static NSString *const kRefreshFetchArgsKey = @"requestArgs";
@interface GTMOAuth2Authentication ()
@property (retain) NSMutableArray *authorizationQueue;
@property (readonly) NSString *authorizationToken;
- (void)setKeysForResponseJSONData:(NSData *)data;
@@ -133,8 +134,6 @@ static NSString *const kRefreshFetchArgsKey = @"requestArgs";
- (void)updateExpirationDate;
- (NSDictionary *)dictionaryWithJSONData:(NSData *)data;
- (void)tokenFetcher:(GTMHTTPFetcher *)fetcher
finishedWithData:(NSData *)data
error:(NSError *)error;
@@ -164,9 +163,11 @@ finishedRefreshWithFetcher:(GTMHTTPFetcher *)fetcher
clientSecret = clientSecret_,
redirectURI = redirectURI_,
parameters = parameters_,
authorizationTokenKey = authorizationTokenKey_,
tokenURL = tokenURL_,
expirationDate = expirationDate_,
additionalTokenRequestParameters = additionalTokenRequestParameters_,
additionalGrantTypeRequestParameters = additionalGrantTypeRequestParameters_,
refreshFetcher = refreshFetcher_,
fetcherService = fetcherService_,
parserClass = parserClass_,
@@ -237,9 +238,11 @@ finishedRefreshWithFetcher:(GTMHTTPFetcher *)fetcher
[clientSecret_ release];
[redirectURI_ release];
[parameters_ release];
[authorizationTokenKey_ release];
[tokenURL_ release];
[expirationDate_ release];
[additionalTokenRequestParameters_ release];
[additionalGrantTypeRequestParameters_ release];
[refreshFetcher_ release];
[authorizationQueue_ release];
[userData_ release];
@@ -290,11 +293,11 @@ finishedRefreshWithFetcher:(GTMHTTPFetcher *)fetcher
}
- (void)setKeysForResponseJSONData:(NSData *)data {
NSDictionary *dict = [self dictionaryWithJSONData:data];
NSDictionary *dict = [[self class] dictionaryWithJSONData:data];
[self setKeysForResponseDictionary:dict];
}
- (NSDictionary *)dictionaryWithJSONData:(NSData *)data {
+ (NSDictionary *)dictionaryWithJSONData:(NSData *)data {
NSMutableDictionary *obj = nil;
NSError *error = nil;
@@ -437,12 +440,32 @@ finishedRefreshWithFetcher:(GTMHTTPFetcher *)fetcher
BOOL hasAccessToken = ([self.accessToken length] > 0);
NSString *noteName;
NSDictionary *userInfo = nil;
if (hasAccessToken && error == nil) {
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc postNotificationName:kGTMOAuth2AccessTokenRefreshed
object:self
userInfo:nil];
// Successful refresh.
noteName = kGTMOAuth2AccessTokenRefreshed;
userInfo = nil;
} else {
// Google's OAuth 2 implementation returns a 400 with JSON body
// containing error key "invalid_grant" to indicate the refresh token
// is invalid or has been revoked by the user. We'll promote the
// JSON error key's value for easy inspection by the observer.
noteName = kGTMOAuth2AccessTokenRefreshFailed;
NSString *jsonErr = nil;
if ([error code] == kGTMHTTPFetcherStatusBadRequest) {
NSDictionary *json = [[error userInfo] objectForKey:kGTMOAuth2ErrorJSONKey];
jsonErr = [json objectForKey:kGTMOAuth2ErrorMessageKey];
}
// error and jsonErr may be nil
userInfo = [NSMutableDictionary dictionary];
[userInfo setValue:error forKey:kGTMOAuth2ErrorObjectKey];
[userInfo setValue:jsonErr forKey:kGTMOAuth2ErrorMessageKey];
}
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc postNotificationName:noteName
object:self
userInfo:userInfo];
for (GTMOAuth2AuthorizationArgs *args in pendingAuthQueue) {
if (!hasAccessToken && args.error == nil) {
@@ -522,7 +545,8 @@ finishedRefreshWithFetcher:(GTMHTTPFetcher *)fetcher
#endif
}
NSString *accessToken = self.accessToken;
// Get the access token.
NSString *accessToken = self.authorizationToken;
if (isAuthorizableRequest && [accessToken length] > 0) {
if (request) {
// we have a likely valid access token
@@ -620,8 +644,8 @@ finishedRefreshWithFetcher:(GTMHTTPFetcher *)fetcher
NSString *token = self.refreshToken;
if (token == nil) {
// For services which do not support refresh tokens, we'll just check
// the access token
token = self.accessToken;
// the access token.
token = self.authorizationToken;
}
BOOL canAuth = [token length] > 0;
return canAuth;
@@ -699,10 +723,11 @@ finishedRefreshWithFetcher:(GTMHTTPFetcher *)fetcher
NSString *refreshToken = self.refreshToken;
NSString *code = self.code;
NSString *assertion = self.assertion;
NSString *grantType = nil;
if (refreshToken) {
// We have a refresh token
[paramsDict setObject:@"refresh_token" forKey:@"grant_type"];
grantType = @"refresh_token";
[paramsDict setObject:refreshToken forKey:@"refresh_token"];
NSString *refreshScope = self.refreshScope;
@@ -713,7 +738,7 @@ finishedRefreshWithFetcher:(GTMHTTPFetcher *)fetcher
fetchType = kGTMOAuth2FetchTypeRefresh;
} else if (code) {
// We have a code string
[paramsDict setObject:@"authorization_code" forKey:@"grant_type"];
grantType = @"authorization_code";
[paramsDict setObject:code forKey:@"code"];
NSString *redirectURI = self.redirectURI;
@@ -729,9 +754,8 @@ finishedRefreshWithFetcher:(GTMHTTPFetcher *)fetcher
fetchType = kGTMOAuth2FetchTypeToken;
} else if (assertion) {
// We have an assertion string
grantType = @"http://oauth.net/grant_type/jwt/1.0/bearer";
[paramsDict setObject:assertion forKey:@"assertion"];
[paramsDict setObject:@"http://oauth.net/grant_type/jwt/1.0/bearer"
forKey:@"grant_type"];
fetchType = kGTMOAuth2FetchTypeAssertion;
} else {
#if DEBUG
@@ -739,7 +763,8 @@ finishedRefreshWithFetcher:(GTMHTTPFetcher *)fetcher
#endif
return nil;
}
[paramsDict setObject:grantType forKey:@"grant_type"];
NSString *clientID = self.clientID;
if ([clientID length] > 0) {
[paramsDict setObject:clientID forKey:@"client_id"];
@@ -754,6 +779,11 @@ finishedRefreshWithFetcher:(GTMHTTPFetcher *)fetcher
if (additionalParams) {
[paramsDict addEntriesFromDictionary:additionalParams];
}
NSDictionary *grantTypeParams =
[self.additionalGrantTypeRequestParameters objectForKey:grantType];
if (grantTypeParams) {
[paramsDict addEntriesFromDictionary:grantTypeParams];
}
NSString *paramStr = [[self class] encodedQueryParametersForDictionary:paramsDict];
NSData *paramData = [paramStr dataUsingEncoding:NSUTF8StringEncoding];
@@ -821,11 +851,11 @@ finishedRefreshWithFetcher:(GTMHTTPFetcher *)fetcher
BOOL hasData = ([data length] > 0);
if (error) {
// Failed; if the error body is JSON, parse it and add it to the error's
// userInfo dictionary
// Failed. If the error body is JSON, parse it and add it to the error's
// userInfo dictionary.
if (hasData) {
if (isResponseJSON) {
NSDictionary *errorJson = [self dictionaryWithJSONData:data];
NSDictionary *errorJson = [[self class] dictionaryWithJSONData:data];
if ([errorJson count] > 0) {
#if DEBUG
NSLog(@"Error %@\nError data:\n%@", error, errorJson);
@@ -845,7 +875,7 @@ finishedRefreshWithFetcher:(GTMHTTPFetcher *)fetcher
}
}
} else {
// Succeeded; we have an access token
// Succeeded; we have the requested token.
#if DEBUG
NSAssert(hasData, @"data missing in token response");
#endif
@@ -960,10 +990,24 @@ finishedRefreshWithFetcher:(GTMHTTPFetcher *)fetcher
self.expirationDate = nil;
self.userEmail = nil;
self.userEmailIsVerified = nil;
self.authorizationTokenKey = nil;
}
#pragma mark Accessors for Response Parameters
- (NSString *)authorizationToken {
// The token used for authorization is typically the access token unless
// the user has specified that an alternative parameter be used.
NSString *authorizationToken;
NSString *authTokenKey = self.authorizationTokenKey;
if (authTokenKey != nil) {
authorizationToken = [self.parameters objectForKey:authTokenKey];
} else {
authorizationToken = self.accessToken;
}
return authorizationToken;
}
- (NSString *)accessToken {
return [self.parameters objectForKey:kOAuth2AccessTokenKey];
}

View File

@@ -29,10 +29,6 @@ static const NSTimeInterval kDefaultNetworkLossTimeoutInterval = 30.0;
NSString *const kOOBString = @"urn:ietf:wg:oauth:2.0:oob";
@interface GTMOAuth2Authentication (InternalMethods)
- (NSDictionary *)dictionaryWithJSONData:(NSData *)data;
@end
@interface GTMOAuth2SignIn ()
@property (assign) BOOL hasHandledCallback;
@property (retain) GTMHTTPFetcher *pendingFetcher;
@@ -68,6 +64,8 @@ finishedWithFetcher:(GTMHTTPFetcher *)fetcher
- (void)reachabilityTarget:(SCNetworkReachabilityRef)reachabilityRef
changedFlags:(SCNetworkConnectionFlags)flags;
- (void)reachabilityTimerFired:(NSTimer *)timer;
+ (NSData *)decodeWebSafeBase64:(NSString *)base64Str;
@end
@implementation GTMOAuth2SignIn
@@ -559,12 +557,17 @@ finishedWithFetcher:(GTMHTTPFetcher *)fetcher
NSURL *infoURL = [[self class] googleUserInfoURL];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:infoURL];
NSString *userAgent = [auth userAgent];
[request setValue:userAgent forHTTPHeaderField:@"User-Agent"];
if ([auth respondsToSelector:@selector(userAgent)]) {
NSString *userAgent = [auth userAgent];
[request setValue:userAgent forHTTPHeaderField:@"User-Agent"];
}
[request setValue:@"no-cache" forHTTPHeaderField:@"Cache-Control"];
GTMHTTPFetcher *fetcher;
id <GTMHTTPFetcherServiceProtocol> fetcherService = auth.fetcherService;
id <GTMHTTPFetcherServiceProtocol> fetcherService = nil;
if ([auth respondsToSelector:@selector(fetcherService)]) {
fetcherService = auth.fetcherService;
};
if (fetcherService) {
fetcher = [fetcherService fetcherWithRequest:request];
} else {
@@ -578,7 +581,36 @@ finishedWithFetcher:(GTMHTTPFetcher *)fetcher
}
- (void)fetchGoogleUserInfo {
// fetch the user's email address or profile
if (!self.shouldFetchGoogleUserProfile) {
// If we only need email and user ID, not the full profile, and we have an
// id_token, it may have the email and user ID so we won't need to fetch
// them.
GTMOAuth2Authentication *auth = self.authentication;
NSString *idToken = [auth.parameters objectForKey:@"id_token"];
if ([idToken length] > 0) {
// The id_token has three dot-delimited parts. The second is the
// JSON profile.
//
// http://www.tbray.org/ongoing/When/201x/2013/04/04/ID-Tokens
NSArray *parts = [idToken componentsSeparatedByString:@"."];
if ([parts count] == 3) {
NSString *part2 = [parts objectAtIndex:1];
if ([part2 length] > 0) {
NSData *data = [[self class] decodeWebSafeBase64:part2];
if ([data length] > 0) {
[self updateGoogleUserInfoWithData:data];
if ([[auth userID] length] > 0 && [[auth userEmail] length] > 0) {
// We obtained user ID and email from the ID token.
[self finishSignInWithError:nil];
return;
}
}
}
}
}
}
// Fetch the email and profile from the userinfo endpoint.
GTMOAuth2Authentication *auth = self.authentication;
GTMHTTPFetcher *fetcher = [[self class] userInfoFetcherWithAuth:auth];
[fetcher beginFetchWithDelegate:self
@@ -611,27 +643,40 @@ finishedWithFetcher:(GTMHTTPFetcher *)fetcher
#endif
} else {
// We have the authenticated user's info
if (data) {
NSDictionary *profileDict = [auth dictionaryWithJSONData:data];
if (profileDict) {
self.userProfile = profileDict;
// Save the ID into the auth object
NSString *identifier = [profileDict objectForKey:@"id"];
[auth setUserID:identifier];
// Save the email into the auth object
NSString *email = [profileDict objectForKey:@"email"];
[auth setUserEmail:email];
NSNumber *verified = [profileDict objectForKey:@"verified_email"];
[auth setUserEmailIsVerified:[verified stringValue]];
}
}
[self updateGoogleUserInfoWithData:data];
}
[self finishSignInWithError:error];
}
- (void)updateGoogleUserInfoWithData:(NSData *)data {
if (!data) return;
GTMOAuth2Authentication *auth = self.authentication;
NSDictionary *profileDict = [[auth class] dictionaryWithJSONData:data];
if (profileDict) {
self.userProfile = profileDict;
// Save the ID into the auth object
NSString *identifier = [profileDict objectForKey:@"id"];
[auth setUserID:identifier];
// Save the email into the auth object
NSString *email = [profileDict objectForKey:@"email"];
[auth setUserEmail:email];
// The verified_email key is a boolean NSNumber in the userinfo
// endpoint response, but it is a string like "true" in the id_token.
// We want to consistently save it as a string of the boolean value,
// like @"1".
id verified = [profileDict objectForKey:@"verified_email"];
if ([verified isKindOfClass:[NSString class]]) {
verified = [NSNumber numberWithBool:[verified boolValue]];
}
[auth setUserEmailIsVerified:[verified stringValue]];
}
}
#endif // !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT
- (void)finishSignInWithError:(NSError *)error {
@@ -828,6 +873,62 @@ static void ReachabilityCallBack(SCNetworkReachabilityRef target,
}
[auth reset];
}
// Based on Cyrus Najmabadi's elegent little encoder and decoder from
// http://www.cocoadev.com/index.pl?BaseSixtyFour and on GTLBase64
+ (NSData *)decodeWebSafeBase64:(NSString *)base64Str {
static char decodingTable[128];
static BOOL hasInited = NO;
if (!hasInited) {
char webSafeEncodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
memset(decodingTable, 0, 128);
for (unsigned int i = 0; i < sizeof(webSafeEncodingTable); i++) {
decodingTable[(unsigned int) webSafeEncodingTable[i]] = (char)i;
}
hasInited = YES;
}
// The input string should be plain ASCII.
const char *cString = [base64Str cStringUsingEncoding:NSASCIIStringEncoding];
if (cString == nil) return nil;
NSInteger inputLength = (NSInteger)strlen(cString);
// Input length is not being restricted to multiples of 4.
if (inputLength == 0) return [NSData data];
while (inputLength > 0 && cString[inputLength - 1] == '=') {
inputLength--;
}
NSInteger outputLength = inputLength * 3 / 4;
NSMutableData* data = [NSMutableData dataWithLength:(NSUInteger)outputLength];
uint8_t *output = [data mutableBytes];
NSInteger inputPoint = 0;
NSInteger outputPoint = 0;
char *table = decodingTable;
while (inputPoint < inputLength - 1) {
int i0 = cString[inputPoint++];
int i1 = cString[inputPoint++];
int i2 = inputPoint < inputLength ? cString[inputPoint++] : 'A'; // 'A' will decode to \0
int i3 = inputPoint < inputLength ? cString[inputPoint++] : 'A';
output[outputPoint++] = (uint8_t)((table[i0] << 2) | (table[i1] >> 4));
if (outputPoint < outputLength) {
output[outputPoint++] = (uint8_t)(((table[i1] & 0xF) << 4) | (table[i2] >> 2));
}
if (outputPoint < outputLength) {
output[outputPoint++] = (uint8_t)(((table[i2] & 0x3) << 6) | table[i3]);
}
}
return data;
}
#endif // !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT
@end

View File

@@ -45,10 +45,11 @@
_EXTERN NSString* const kGTMOAuth2KeychainErrorDomain _INITIALIZE_AS(@"com.google.GTMOAuthKeychain");
@class GTMOAuth2SignIn;
@class GTMOAuth2ViewControllerTouch;
typedef void (^GTMOAuth2ViewControllerCompletionHandler)(GTMOAuth2ViewControllerTouch *viewController, GTMOAuth2Authentication *auth, NSError *error);
@interface GTMOAuth2ViewControllerTouch : UIViewController<UINavigationControllerDelegate, UIWebViewDelegate> {
@private
UIButton *backButton_;
@@ -73,7 +74,7 @@ _EXTERN NSString* const kGTMOAuth2KeychainErrorDomain _INITIALIZE_AS(@"com
SEL finishedSelector_;
#if NS_BLOCKS_AVAILABLE
void (^completionBlock_)(GTMOAuth2ViewControllerTouch *, GTMOAuth2Authentication *, NSError *);
GTMOAuth2ViewControllerCompletionHandler completionBlock_;
void (^popViewBlock_)(void);
#endif
@@ -168,7 +169,7 @@ _EXTERN NSString* const kGTMOAuth2KeychainErrorDomain _INITIALIZE_AS(@"com
#endif
// the default timeout for an unreachable network during display of the
// sign-in page is 10 seconds; set this to 0 to have no timeout
// sign-in page is 30 seconds; set this to 0 to have no timeout
@property (nonatomic, assign) NSTimeInterval networkLossTimeoutInterval;
// if set, cookies are deleted for this URL when the view is hidden
@@ -228,13 +229,13 @@ _EXTERN NSString* const kGTMOAuth2KeychainErrorDomain _INITIALIZE_AS(@"com
clientID:(NSString *)clientID
clientSecret:(NSString *)clientSecret
keychainItemName:(NSString *)keychainItemName
completionHandler:(void (^)(GTMOAuth2ViewControllerTouch *viewController, GTMOAuth2Authentication *auth, NSError *error))handler;
completionHandler:(GTMOAuth2ViewControllerCompletionHandler)handler;
- (id)initWithScope:(NSString *)scope
clientID:(NSString *)clientID
clientSecret:(NSString *)clientSecret
keychainItemName:(NSString *)keychainItemName
completionHandler:(void (^)(GTMOAuth2ViewControllerTouch *viewController, GTMOAuth2Authentication *auth, NSError *error))handler;
completionHandler:(GTMOAuth2ViewControllerCompletionHandler)handler;
#endif
#endif
@@ -257,12 +258,12 @@ _EXTERN NSString* const kGTMOAuth2KeychainErrorDomain _INITIALIZE_AS(@"com
+ (id)controllerWithAuthentication:(GTMOAuth2Authentication *)auth
authorizationURL:(NSURL *)authorizationURL
keychainItemName:(NSString *)keychainItemName // may be nil
completionHandler:(void (^)(GTMOAuth2ViewControllerTouch *viewController, GTMOAuth2Authentication *auth, NSError *error))handler;
completionHandler:(GTMOAuth2ViewControllerCompletionHandler)handler;
- (id)initWithAuthentication:(GTMOAuth2Authentication *)auth
authorizationURL:(NSURL *)authorizationURL
keychainItemName:(NSString *)keychainItemName
completionHandler:(void (^)(GTMOAuth2ViewControllerTouch *viewController, GTMOAuth2Authentication *auth, NSError *error))handler;
completionHandler:(GTMOAuth2ViewControllerCompletionHandler)handler;
#endif
// subclasses may override authNibName to specify a custom name
@@ -271,6 +272,10 @@ _EXTERN NSString* const kGTMOAuth2KeychainErrorDomain _INITIALIZE_AS(@"com
// subclasses may override authNibBundle to specify a custom bundle
+ (NSBundle *)authNibBundle;
// subclasses may override setUpNavigation to provide their own navigation
// controls
- (void)setUpNavigation;
// apps may replace the sign-in class with their own subclass of it
+ (Class)signInClass;
+ (void)setSignInClass:(Class)theClass;

View File

@@ -113,7 +113,7 @@ finishedWithAuth:(GTMOAuth2Authentication *)auth
clientID:(NSString *)clientID
clientSecret:(NSString *)clientSecret
keychainItemName:(NSString *)keychainItemName
completionHandler:(void (^)(GTMOAuth2ViewControllerTouch *viewController, GTMOAuth2Authentication *auth, NSError *error))handler {
completionHandler:(GTMOAuth2ViewControllerCompletionHandler)handler {
return [[[self alloc] initWithScope:scope
clientID:clientID
clientSecret:clientSecret
@@ -125,7 +125,7 @@ finishedWithAuth:(GTMOAuth2Authentication *)auth
clientID:(NSString *)clientID
clientSecret:(NSString *)clientSecret
keychainItemName:(NSString *)keychainItemName
completionHandler:(void (^)(GTMOAuth2ViewControllerTouch *viewController, GTMOAuth2Authentication *auth, NSError *error))handler {
completionHandler:(GTMOAuth2ViewControllerCompletionHandler)handler {
// convenient entry point for Google authentication
Class signInClass = [[self class] signInClass];
@@ -206,7 +206,7 @@ finishedWithAuth:(GTMOAuth2Authentication *)auth
+ (id)controllerWithAuthentication:(GTMOAuth2Authentication *)auth
authorizationURL:(NSURL *)authorizationURL
keychainItemName:(NSString *)keychainItemName
completionHandler:(void (^)(GTMOAuth2ViewControllerTouch *viewController, GTMOAuth2Authentication *auth, NSError *error))handler {
completionHandler:(GTMOAuth2ViewControllerCompletionHandler)handler {
return [[[self alloc] initWithAuthentication:auth
authorizationURL:authorizationURL
keychainItemName:keychainItemName
@@ -216,7 +216,7 @@ finishedWithAuth:(GTMOAuth2Authentication *)auth
- (id)initWithAuthentication:(GTMOAuth2Authentication *)auth
authorizationURL:(NSURL *)authorizationURL
keychainItemName:(NSString *)keychainItemName
completionHandler:(void (^)(GTMOAuth2ViewControllerTouch *viewController, GTMOAuth2Authentication *auth, NSError *error))handler {
completionHandler:(GTMOAuth2ViewControllerCompletionHandler)handler {
// fall back to the non-blocks init
self = [self initWithAuthentication:auth
authorizationURL:authorizationURL
@@ -365,6 +365,10 @@ finishedWithAuth:(GTMOAuth2Authentication *)auth
- (void)viewDidLoad {
[self setUpNavigation];
}
- (void)setUpNavigation {
rightBarButtonItem_.customView = navButtonsView_;
self.navigationItem.rightBarButtonItem = rightBarButtonItem_;
}
@@ -670,6 +674,9 @@ static Class gSignInClass = Nil;
#pragma mark Protocol implementations
- (void)viewWillAppear:(BOOL)animated {
// See the comment on clearBrowserCookies in viewDidDisappear.
[self clearBrowserCookies];
if (!isViewShown_) {
isViewShown_ = YES;
if ([self isNavigationBarTranslucent]) {
@@ -713,13 +720,18 @@ static Class gSignInClass = Nil;
#endif
}
// prevent the next sign-in from showing in the WebView that the user is
// already signed in
[self clearBrowserCookies];
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
// prevent the next sign-in from showing in the WebView that the user is
// already signed in. It's possible for the WebView to set the cookies even
// after this, so we also clear them when the view first appears.
[self clearBrowserCookies];
}
- (void)viewDidLayoutSubviews {
// We don't call super's version of this method because
// -[UIViewController viewDidLayoutSubviews] is documented as a no-op, that

View File

@@ -0,0 +1,44 @@
//
// GoogleOpenSource.h
// Google+ iOS SDK
//
// Copyright 2013 Google Inc.
//
// Use of this SDK is subject to the Google+ Platform Terms of Service:
// https://developers.google.com/+/terms
//
// GTM.
#import "GTMDefines.h"
#import "GTMHTTPFetcher.h"
#import "GTMHTTPFetcherService.h"
#import "GTMHTTPFetchHistory.h"
#import "GTMLogger.h"
#import "GTMMethodCheck.h"
#import "GTMNSDictionary+URLArguments.h"
#import "GTMNSString+URLArguments.h"
#import "GTMOAuth2Authentication.h"
#import "GTMOAuth2SignIn.h"
#import "GTMOAuth2ViewControllerTouch.h"
#import "GTMObjC2Runtime.h"
// Chrome.
#import "OpenInChromeController.h"
// GTL.
#import "GTLDefines.h"
#import "GTLBatchQuery.h"
#import "GTLBatchResult.h"
#import "GTLDateTime.h"
#import "GTLErrorObject.h"
#import "GTLObject.h"
#import "GTLQuery.h"
#import "GTLRuntimeCommon.h"
#import "GTLService.h"
#import "GTLFramework.h"
#import "GTLJSONParser.h"
#import "GTLUtilities.h"
// GTLPlus.
#import "GTLPlus.h"