From 6c661b226aac0cfcb4364d2509b13919074505da Mon Sep 17 00:00:00 2001 From: Igor Khmurets Date: Sun, 13 Jul 2014 01:39:02 +0200 Subject: [PATCH] [ios] Added GooglePlus support to AccountManager --- iphone/Maps/AccountManager.m | 78 + iphone/Maps/GooglePlusSDK/GPPDeepLink.h | 59 + iphone/Maps/GooglePlusSDK/GPPShare.h | 154 ++ iphone/Maps/GooglePlusSDK/GPPSignIn.h | 150 + iphone/Maps/GooglePlusSDK/GPPSignInButton.h | 44 + iphone/Maps/GooglePlusSDK/GPPURLHandler.h | 25 + .../GooglePlusSDK/OpenSource/GTL/GTLBase64.h | 25 + .../GooglePlusSDK/OpenSource/GTL/GTLBase64.m | 139 + .../OpenSource/GTL/GTLBatchQuery.h | 52 + .../OpenSource/GTL/GTLBatchQuery.m | 133 + .../OpenSource/GTL/GTLBatchResult.h | 58 + .../OpenSource/GTL/GTLBatchResult.m | 92 + .../OpenSource/GTL/GTLDateTime.h | 60 + .../OpenSource/GTL/GTLDateTime.m | 471 ++++ .../GooglePlusSDK/OpenSource/GTL/GTLDefines.h | 144 + .../OpenSource/GTL/GTLErrorObject.h | 45 + .../OpenSource/GTL/GTLErrorObject.m | 78 + .../OpenSource/GTL/GTLFramework.h | 35 + .../OpenSource/GTL/GTLFramework.m | 40 + .../OpenSource/GTL/GTLJSONParser.h | 41 + .../OpenSource/GTL/GTLJSONParser.m | 150 + .../GooglePlusSDK/OpenSource/GTL/GTLObject.h | 208 ++ .../GooglePlusSDK/OpenSource/GTL/GTLObject.m | 722 +++++ .../OpenSource/GTL/GTLPlus/GTLPlus.h | 44 + .../OpenSource/GTL/GTLPlus/GTLPlusAcl.h | 60 + .../OpenSource/GTL/GTLPlus/GTLPlusAcl.m | 61 + .../GTL/GTLPlus/GTLPlusAclentryResource.h | 61 + .../GTL/GTLPlus/GTLPlusAclentryResource.m | 48 + .../OpenSource/GTL/GTLPlus/GTLPlusActivity.h | 493 ++++ .../OpenSource/GTL/GTLPlus/GTLPlusActivity.m | 290 ++ .../GTL/GTLPlus/GTLPlusActivityFeed.h | 81 + .../GTL/GTLPlus/GTLPlusActivityFeed.m | 64 + .../OpenSource/GTL/GTLPlus/GTLPlusComment.h | 183 ++ .../OpenSource/GTL/GTLPlus/GTLPlusComment.m | 133 + .../GTL/GTLPlus/GTLPlusCommentFeed.h | 78 + .../GTL/GTLPlus/GTLPlusCommentFeed.m | 63 + .../OpenSource/GTL/GTLPlus/GTLPlusConstants.h | 57 + .../OpenSource/GTL/GTLPlus/GTLPlusConstants.m | 49 + .../OpenSource/GTL/GTLPlus/GTLPlusItemScope.h | 225 ++ .../OpenSource/GTL/GTLPlus/GTLPlusItemScope.m | 77 + .../OpenSource/GTL/GTLPlus/GTLPlusMoment.h | 65 + .../OpenSource/GTL/GTLPlus/GTLPlusMoment.m | 54 + .../GTL/GTLPlus/GTLPlusMomentsFeed.h | 76 + .../GTL/GTLPlus/GTLPlusMomentsFeed.m | 61 + .../GTL/GTLPlus/GTLPlusPeopleFeed.h | 76 + .../GTL/GTLPlus/GTLPlusPeopleFeed.m | 61 + .../OpenSource/GTL/GTLPlus/GTLPlusPerson.h | 388 +++ .../OpenSource/GTL/GTLPlus/GTLPlusPerson.m | 189 ++ .../OpenSource/GTL/GTLPlus/GTLQueryPlus.h | 297 ++ .../OpenSource/GTL/GTLPlus/GTLQueryPlus.m | 182 ++ .../OpenSource/GTL/GTLPlus/GTLServicePlus.h | 61 + .../OpenSource/GTL/GTLPlus/GTLServicePlus.m | 71 + .../GooglePlusSDK/OpenSource/GTL/GTLQuery.h | 135 + .../GooglePlusSDK/OpenSource/GTL/GTLQuery.m | 267 ++ .../OpenSource/GTL/GTLRuntimeCommon.h | 57 + .../OpenSource/GTL/GTLRuntimeCommon.m | 1141 ++++++++ .../GooglePlusSDK/OpenSource/GTL/GTLService.h | 607 +++++ .../GooglePlusSDK/OpenSource/GTL/GTLService.m | 2407 +++++++++++++++++ .../OpenSource/GTL/GTLTargetNamespace.h | 58 + .../OpenSource/GTL/GTLUploadParameters.h | 60 + .../OpenSource/GTL/GTLUploadParameters.m | 107 + .../OpenSource/GTL/GTLUtilities.h | 93 + .../OpenSource/GTL/GTLUtilities.m | 358 +++ .../GooglePlusSDK/OpenSource/GTMDefines.h | 441 +++ .../OpenSource/GTMGarbageCollection.h | 72 + .../OpenSource/GTMHTTPFetchHistory.h | 187 ++ .../OpenSource/GTMHTTPFetchHistory.m | 605 +++++ .../GooglePlusSDK/OpenSource/GTMHTTPFetcher.h | 748 +++++ .../GooglePlusSDK/OpenSource/GTMHTTPFetcher.m | 1935 +++++++++++++ .../OpenSource/GTMHTTPFetcherLogging.h | 98 + .../OpenSource/GTMHTTPFetcherLogging.m | 1101 ++++++++ .../OpenSource/GTMHTTPFetcherService.h | 125 + .../OpenSource/GTMHTTPFetcherService.m | 490 ++++ .../Maps/GooglePlusSDK/OpenSource/GTMLogger.h | 504 ++++ .../Maps/GooglePlusSDK/OpenSource/GTMLogger.m | 612 +++++ .../GooglePlusSDK/OpenSource/GTMMethodCheck.h | 88 + .../GooglePlusSDK/OpenSource/GTMMethodCheck.m | 174 ++ .../OpenSource/GTMNSDictionary+URLArguments.h | 36 + .../OpenSource/GTMNSDictionary+URLArguments.m | 71 + .../OpenSource/GTMNSString+URLArguments.h | 41 + .../OpenSource/GTMNSString+URLArguments.m | 45 + .../OpenSource/GTMOAuth2Authentication.h | 333 +++ .../OpenSource/GTMOAuth2Authentication.m | 1231 +++++++++ .../OpenSource/GTMOAuth2SignIn.h | 187 ++ .../OpenSource/GTMOAuth2SignIn.m | 835 ++++++ .../OpenSource/GTMOAuth2ViewControllerTouch.h | 361 +++ .../OpenSource/GTMOAuth2ViewControllerTouch.m | 1035 +++++++ .../OpenSource/GTMOAuth2ViewTouch.xib | 494 ++++ .../OpenSource/GTMObjC2Runtime.h | 113 + .../OpenSource/GTMObjC2Runtime.m | 163 ++ .../OpenSource/OpenInChromeController.h | 54 + .../OpenSource/OpenInChromeController.m | 135 + iphone/Maps/GooglePlusSDK/libGooglePlus.a | Bin 0 -> 668920 bytes .../GooglePlusSDK/libGooglePlusUniversal.a | Bin 0 -> 984556 bytes iphone/Maps/Maps.xcodeproj/project.pbxproj | 457 ++++ iphone/Maps/MapsWithMe-Lite.plist | 40 +- iphone/Maps/MapsWithMe-Pro.plist | 60 +- 97 files changed, 24792 insertions(+), 20 deletions(-) create mode 100644 iphone/Maps/GooglePlusSDK/GPPDeepLink.h create mode 100644 iphone/Maps/GooglePlusSDK/GPPShare.h create mode 100644 iphone/Maps/GooglePlusSDK/GPPSignIn.h create mode 100644 iphone/Maps/GooglePlusSDK/GPPSignInButton.h create mode 100644 iphone/Maps/GooglePlusSDK/GPPURLHandler.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLBase64.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLBase64.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLBatchQuery.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLBatchQuery.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLBatchResult.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLBatchResult.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLDateTime.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLDateTime.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLDefines.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLErrorObject.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLErrorObject.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLFramework.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLFramework.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLJSONParser.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLJSONParser.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLObject.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLObject.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlus.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusAcl.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusAcl.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusAclentryResource.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusAclentryResource.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusActivity.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusActivity.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusActivityFeed.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusActivityFeed.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusComment.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusComment.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusCommentFeed.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusCommentFeed.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusConstants.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusConstants.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusItemScope.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusItemScope.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusMoment.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusMoment.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusMomentsFeed.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusMomentsFeed.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusPeopleFeed.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusPeopleFeed.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusPerson.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusPerson.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLQueryPlus.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLQueryPlus.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLServicePlus.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLServicePlus.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLQuery.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLQuery.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLRuntimeCommon.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLRuntimeCommon.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLService.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLService.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLTargetNamespace.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLUploadParameters.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLUploadParameters.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLUtilities.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLUtilities.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTMDefines.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTMGarbageCollection.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTMHTTPFetchHistory.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTMHTTPFetchHistory.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTMHTTPFetcher.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTMHTTPFetcher.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTMHTTPFetcherLogging.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTMHTTPFetcherLogging.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTMHTTPFetcherService.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTMHTTPFetcherService.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTMLogger.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTMLogger.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTMMethodCheck.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTMMethodCheck.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTMNSDictionary+URLArguments.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTMNSDictionary+URLArguments.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTMNSString+URLArguments.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTMNSString+URLArguments.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTMOAuth2Authentication.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTMOAuth2Authentication.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTMOAuth2SignIn.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTMOAuth2SignIn.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTMOAuth2ViewControllerTouch.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTMOAuth2ViewControllerTouch.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTMOAuth2ViewTouch.xib create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTMObjC2Runtime.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/GTMObjC2Runtime.m create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/OpenInChromeController.h create mode 100644 iphone/Maps/GooglePlusSDK/OpenSource/OpenInChromeController.m create mode 100644 iphone/Maps/GooglePlusSDK/libGooglePlus.a create mode 100644 iphone/Maps/GooglePlusSDK/libGooglePlusUniversal.a diff --git a/iphone/Maps/AccountManager.m b/iphone/Maps/AccountManager.m index d592101423..d8af330c47 100644 --- a/iphone/Maps/AccountManager.m +++ b/iphone/Maps/AccountManager.m @@ -1,12 +1,24 @@ #import "AccountManager.h" #import +#import "GPPSignIn.h" +#import "GTLPlusConstants.h" +#import "GPPURLHandler.h" +#import "GPPShare.h" @implementation PostMessage @end +@interface AccountManager () + +@property (nonatomic, copy) CompletionBlock googlePlusLoginCompletion; +@property (nonatomic, copy) CompletionBlock googlePlusShareCompletion; +@property (nonatomic) PostMessage * googlePlusMessage; + +@end + @implementation AccountManager + (instancetype)sharedManager @@ -32,11 +44,33 @@ } case AccountTypeGooglePlus: { + if ([[GPPSignIn sharedInstance] hasAuthInKeychain]) + { + self.googlePlusMessage = nil; + self.googlePlusShareCompletion = block; + + [GPPShare sharedInstance].delegate = self; + id shareBuilder = [[GPPShare sharedInstance] shareDialog]; + [shareBuilder setPrefillText:message.info]; + [shareBuilder open]; + } + else + { + self.googlePlusMessage = message; + [[self googlePlusSignIn] trySilentAuthentication]; + } break; } } } +- (void)finishedSharing:(BOOL)shared +{ + if (self.googlePlusShareCompletion) + self.googlePlusShareCompletion(shared); + + self.googlePlusShareCompletion = nil; +} - (void)postMessage:(PostMessage *)message toFacebookAndCheckPermissionsWithCompletion:(CompletionBlock)block { @@ -172,11 +206,52 @@ } case AccountTypeGooglePlus: { + self.googlePlusLoginCompletion = block; + [[self googlePlusSignIn] authenticate]; break; } } } +- (GPPSignIn *)googlePlusSignIn +{ + GPPSignIn * signIn = [GPPSignIn sharedInstance]; + signIn.clientID = [self googlePlusClientId]; + signIn.scopes = @[kGTLAuthScopePlusLogin]; + signIn.shouldFetchGoogleUserEmail = YES; + signIn.shouldFetchGoogleUserID = YES; + signIn.delegate = self; + return signIn; +} + +- (void)finishedWithAuth:(GTMOAuth2Authentication *)auth error:(NSError *)error +{ + if (self.googlePlusMessage) + [self postMessage:self.googlePlusMessage toAccountType:AccountTypeGooglePlus completion:self.googlePlusShareCompletion]; + else if (self.googlePlusLoginCompletion) + self.googlePlusLoginCompletion(!error); + + self.googlePlusLoginCompletion = nil; +} + +- (NSString *)googlePlusClientId +{ + NSString * bundleId = [[NSBundle mainBundle] bundleIdentifier]; + if ([bundleId isEqualToString:@"com.mapswithme.full"]) + return @"516376788119-olu39qq0md9h8nd0p8ku35i5oiid78i7.apps.googleusercontent.com"; + else if ([bundleId isEqualToString:@"com.mapswithme.travelguide"]) + return @"516376788119-a0kr4j3767gagjf3dn0pmhqkmg6vnpp5.apps.googleusercontent.com"; + else if ([bundleId isEqualToString:@"com.mapswithme.full.debug"]) + return @"516376788119-taf0agjba07a75dpumrlr31qoa9iu8ag.apps.googleusercontent.com"; + else if ([bundleId isEqualToString:@"com.mapswithme.full.simulator"]) + return @"516376788119-k6fr1m85v6osun317i22qaelb3gevcn8.apps.googleusercontent.com"; + else if ([bundleId isEqualToString:@"com.mapswithme.full.beta"]) + return @"516376788119-hj5sm0s62uul62jgh5f2iqem0e04i3lo.apps.googleusercontent.com"; + else if ([bundleId isEqualToString:@"com.mapswithme.travelguide.beta"]) + return @"516376788119-4bo5vnkl653u2gtlv7sc161ig1om3mu7.apps.googleusercontent.com"; + return nil; +} + + (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { [[FBSession activeSession] setStateChangeHandler:^(FBSession * session, FBSessionState state, NSError * error) { @@ -190,6 +265,9 @@ if ([FBAppCall handleOpenURL:url sourceApplication:sourceApplication]) return YES; + if ([GPPURLHandler handleURL:url sourceApplication:sourceApplication annotation:annotation]) + return YES; + return NO; } diff --git a/iphone/Maps/GooglePlusSDK/GPPDeepLink.h b/iphone/Maps/GooglePlusSDK/GPPDeepLink.h new file mode 100644 index 0000000000..991a950664 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/GPPDeepLink.h @@ -0,0 +1,59 @@ +// +// GPPDeepLink.h +// Google+ iOS SDK +// +// Copyright 2012 Google Inc. +// +// Use of this SDK is subject to the Google+ Platform Terms of Service: +// https://developers.google.com/+/terms +// + +#import + +@class GPPDeepLink; + +// A protocol optionally implemented by the client of |GPPDeepLink|. +@protocol GPPDeepLinkDelegate + +// Notifies the client that a deep link has been received either from +// |readDeepLinkAfterInstall| or |handleURL:sourceApplication:annotation:|. +- (void)didReceiveDeepLink:(GPPDeepLink *)deepLink; + +@end + +// This class handles a deep link within a share posted on Google+. +// For more information on deep links, see +// http://developers.google.com/+/mobile/ios/share . +@interface GPPDeepLink : NSObject + +// Sets the delegate to handle the deep link. ++ (void)setDelegate:(id)delegate; + +// Returns a |GPPDeepLink| for your app to handle, or |nil| if not found. The +// deep-link ID can be obtained from |GPPDeepLink|. It is stored when a user +// clicks a link to your app from a Google+ post, but hasn't yet installed your +// app. The user will be redirected to the App Store to install your app. This +// method should be called on or near your app launch to take the user to +// deep-link ID within your app. The delegate will be called if set and if a +// deep link is found. ++ (GPPDeepLink *)readDeepLinkAfterInstall; + +// This method should be called from your |UIApplicationDelegate|'s +// |application:openURL:sourceApplication:annotation|. Returns +// |GooglePlusDeepLink| if |GooglePlusDeepLink| handled this URL, |nil| +// otherwise. The delegate will be called if set and if a deep link is found. +// Also see |handleURL:sourceApplication:annotation:| in |GPPURLHandler|. ++ (GPPDeepLink *)handleURL:(NSURL *)url + sourceApplication:(NSString *)sourceApplication + annotation:(id)annotation; + +// The deep-link ID in |GPPDeepLink| that was passed to the app. +- (NSString *)deepLinkID; + +// This instance method indicates where the user came from before arriving in +// your app. This method is provided for you to collect engagement metrics. +// For the possible values, see +// http://developers.google.com/+/mobile/ios/source-values . +- (NSString *)source; + +@end diff --git a/iphone/Maps/GooglePlusSDK/GPPShare.h b/iphone/Maps/GooglePlusSDK/GPPShare.h new file mode 100644 index 0000000000..e2c3532177 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/GPPShare.h @@ -0,0 +1,154 @@ +// +// GPPShare.h +// Google+ iOS SDK +// +// Copyright 2012 Google Inc. +// +// Use of this SDK is subject to the Google+ Platform Terms of Service: +// https://developers.google.com/+/terms +// + +// To allow a user to share with Google+, please follow these steps: +// +// 0. Create a project on Google API console, +// https://code.google.com/apis/console . Under "API Access", create a +// client ID as "Installed application" with the type "iOS", and +// register the bundle ID of your app. +// +// 1. Initialize the |GPPSignIn| instance with your registered client ID, +// and get the |GPPShare| instance. +// +// [[GPPSignIn shareInstance] setClientID:myClientID]; +// GPPShare *gppShare = [GPPShare sharedInstance]; +// +// 2. In the code where the share dialog will be opened, +// +// [[gppShare shareDialog] open]; +// +// you can optionally call any of the |GPPShareBuilder| methods before +// calling |open|, for example, if there is a particular URL resource to be +// shared, or if you want to set text to prefill user comment in the share +// dialog, such as: +// +// NSURL *urlToShare = [NSURL URLWithString:@"http://www.google.com/"]; +// NSString *prefillText = @"You probably already know this site..."; +// [[[[gppShare shareDialog] setURLToShare:urlToShare] +// setPrefillText:prefillText] open]; +// +// 3. In the '-info.plist' settings for your app, add a URL type to be +// handled by your app. Make the URL scheme the same as your app bundle ID. +// +// 4. In your application delegate, implement: +// - (BOOL)application:(NSString*)application +// openURL:(NSURL *)url +// sourceApplication:(NSString*)sourceApplication +// annotation:(id)annotation { +// if ([gppShare handleURL:url +// sourceApplication:sourceApplication +// annotation:annotation]) { +// return YES; +// } +// // Other handling code here... +// } +// +// 5. Optionally, if you want to be notified of the result of the share action, +// have a delegate class implement |GPPShareDelegate|, for example: +// +// @interface MyDelegateClass : NSObject; +// +// - (void)finishedSharing:(BOOL)shared { +// // The share action was successful if |shared| is YES. +// } +// +// MyDelegateClass *myDelegate = [[MyDelegateClass alloc] init]; +// gppShare.delegate = myDelegate; + +#import + +@class GPPSignIn; + +// The protocol to receive the result of the share action. +@protocol GPPShareDelegate + +// Reports the status of the share action, |shared| is |YES| if user has +// successfully shared her post, |NO| otherwise, such as if the user canceled +// the post. +- (void)finishedSharing:(BOOL)shared; + +@end + +// The builder protocol to open the share dialog. +// For more information on sharing, see +// http://developers.google.com/+/mobile/ios/share . +@protocol GPPShareBuilder + +// Sets the URL resource to be shared. +- (id)setURLToShare:(NSURL *)urlToShare; + +// Sets the text to prefill user's comment in the share dialog. +- (id)setPrefillText:(NSString *)prefillText; + +// Sets the title, description, and thumbnail URL of the shared content preview +// in the share dialog. Only set these fields if you are sharing with a content +// deep link and don't have a URL resource. |title| is required. +- (id)setTitle:(NSString *)title + description:(NSString *)description + thumbnailURL:(NSURL *)thumbnailURL; + +// Sets the content deep-link ID that takes the user straight to your shared +// content. Only set this field if you want the content deep-linking feature. +// The content deep-link ID can either be a fully qualified URI, or URI path, +// which can be up to 512 characters in length. +- (id)setContentDeepLinkID:(NSString *)contentDeepLinkID; + +// Sets the call-to-action button of the shared content preview. +// The call-to-action button consists of a label, URL, and deep-link ID. +// The |label| is a string key defined under "data-calltoactionlabel" on +// http://developers.google.com/+/web/share/interactive#button_attr_calltoactionlabel +// that maps to the actual button text. +// You must set either the |url| or |deepLinkID|, or both. +// The |url| is where the user is taken to after tapping on the button. +// The |deepLinkID| is the call-to-action deep-link ID that takes the user +// straight to a specific action in your app. It can either be a fully qualified +// URI, or URI path, which can be up to 512 characters in length. +// Note: In order to set the call-to-action button: +// 1. User must have been authenticated with scopes including +// "https://www.googleapis.com/auth/plus.login". +// 2. Either |setURLToShare:| or |setTitle:description:thumbnailURL:| must also +// be called. +- (id)setCallToActionButtonWithLabel:(NSString *)label + URL:(NSURL *)url + deepLinkID:(NSString *)deepLinkID; + +// Opens the share dialog. Returns |NO| if there was an error, |YES| otherwise. +- (BOOL)open; + +@end + +// The primary class for the share action on Google+. +// For more information on sharing, see +// http://developers.google.com/+/mobile/ios/share . +@interface GPPShare : NSObject + +// The object to be notified when the share action has finished. +@property (nonatomic, assign) id delegate; + +// Returns a shared |GPPShare| instance. +// |[GPPSignIn sharedInstance].clientID| must be initialized with a client ID +// registered in the Google API console, https://code.google.com/apis/console/ +// with the app's bundle ID. ++ (GPPShare *)sharedInstance; + +// Returns a share dialog builder instance. Call its |open| method to +// create the dialog after setting the parameters as needed. +- (id)shareDialog; + +// This method should be called from your |UIApplicationDelegate|'s +// |application:openURL:sourceApplication:annotation|. Returns |YES| if +// |GPPShare| handled this URL. +// Also see |handleURL:sourceApplication:annotation:| in |GPPURLHandler|. +- (BOOL)handleURL:(NSURL *)url + sourceApplication:(NSString *)sourceApplication + annotation:(id)annotation; + +@end diff --git a/iphone/Maps/GooglePlusSDK/GPPSignIn.h b/iphone/Maps/GooglePlusSDK/GPPSignIn.h new file mode 100644 index 0000000000..391df12b29 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/GPPSignIn.h @@ -0,0 +1,150 @@ +// +// GPPSignIn.h +// Google+ iOS SDK +// +// Copyright 2012 Google Inc. +// +// Use of this SDK is subject to the Google+ Platform Terms of Service: +// https://developers.google.com/+/terms +// + +#import + +@class GTMOAuth2Authentication; +@class GTMOAuth2ViewControllerTouch; + +// A protocol implemented by the client of |GPPSignIn| to receive a refresh +// token or an error. +@protocol GPPSignInDelegate + +// The authorization has finished and is successful if |error| is |nil|. +- (void)finishedWithAuth:(GTMOAuth2Authentication *)auth + error:(NSError *)error; + +// Finished disconnecting user from the app. +// The operation was successful if |error| is |nil|. +@optional +- (void)didDisconnectWithError:(NSError *)error; + +@end + +// This class signs the user in with Google. It provides single sign-on +// via the Google+ app (if installed), Chrome for iOS (if installed), or Mobile +// Safari. +// +// For reference, please see "Google+ Sign-In for iOS" at +// https://developers.google.com/+/mobile/ios/sign-in . +// Here is sample code to use |GPPSignIn|: +// 1) Get a reference to the |GPPSignIn| shared instance: +// GPPSignIn *signIn = [GPPSignIn sharedInstance]; +// 2) Set the OAuth 2.0 scopes you want to request: +// [signIn setScopes:[NSArray arrayWithObject: +// @"https://www.googleapis.com/auth/plus.login"]]; +// 2) Call [signIn setDelegate:self]; +// 3) Set up delegate method |finishedWithAuth:error:|. +// 4) Call |handleURL| on the shared instance from |application:openUrl:...| +// in your app delegate. +// 5) Call [signIn authenticate]; +@interface GPPSignIn : NSObject + +// The authentication object for the current user, or |nil| if there is +// currently no logged in user. +@property (nonatomic, readonly) GTMOAuth2Authentication *authentication; + +// The Google user ID. It is only available if |shouldFetchGoogleUserID| is set +// and either |trySilentAuthentication| or |authenticate| has been completed +// successfully. +@property (nonatomic, readonly) NSString *userID; + +// The Google user's email. It is only available if |shouldFetchGoogleUserEmail| +// is set and either |trySilentAuthentication| or |authenticate| has been +// completed successfully. +@property (nonatomic, readonly) NSString *userEmail; + +// The object to be notified when authentication is finished. +@property (nonatomic, assign) id delegate; + +// All properties below are optional parameters. If they need to be set, set +// before calling |authenticate|. + +// The client ID of the app from the Google APIs console. +// Must set for sign-in to work. +@property (nonatomic, copy) NSString *clientID; + +// The API scopes requested by the app in an array of |NSString|s. +// The default value is |@[@"https://www.googleapis.com/auth/plus.login"]|. +@property (nonatomic, copy) NSArray *scopes; + +// Whether or not to attempt Single-Sign-On when signing in. +// If |attemptSSO| is true, the sign-in button tries to authenticate with the +// Google+ application if it is installed. If false, it always uses Google+ via +// Chrome for iOS, if installed, or Mobile Safari for authentication. +// The default value is |YES|. +@property (nonatomic, assign) BOOL attemptSSO; + +// The language for sign-in, in the form of ISO 639-1 language code +// optionally followed by a dash and ISO 3166-1 alpha-2 region code, +// such as |@"it"| or |@"pt-PT"|. +// Only set if different from system default. +@property (nonatomic, copy) NSString *language; + +// Name of the keychain to save the sign-in state. +// Only set if a custom name needs to be used. +@property (nonatomic, copy) NSString *keychainName; + +// An |NSString| array of moment types used by your app. Use values from the +// full list at +// https://developers.google.com/+/api/moment-types . +// such as "http://schemas.google.com/AddActivity". +// This property is required only for writing moments, with +// "https://www.googleapis.com/auth/plus.login" as a scope. +@property (nonatomic, copy) NSArray *actions; + +// Whether or not to fetch user email after signing in. The email is saved in +// the |GTMOAuth2Authentication| object. Note that using this flag automatically +// adds "https://www.googleapis.com/auth/userinfo.email" scope to the request. +@property (nonatomic, assign) BOOL shouldFetchGoogleUserEmail; + +// Whether or not to fetch user ID after signing in. The ID can be retrieved +// by |googleUserID| after user has been authenticated. +// Note, a scope, such as "https://www.googleapis.com/auth/plus.login" or +// "https://www.googleapis.com/auth/plus.me", that provides user ID must be +// included in |scopes| for this flag to work. +@property (nonatomic, assign) BOOL shouldFetchGoogleUserID; + +// Returns a shared |GPPSignIn| instance. ++ (GPPSignIn *)sharedInstance; + +// Checks whether the user has either currently signed in or has previous +// authentication saved in keychain. +- (BOOL)hasAuthInKeychain; + +// Attempts to authenticate silently without user interaction. +// Returns |YES| and calls the delegate if the user has either currently signed +// in or has previous authentication saved in keychain. +- (BOOL)trySilentAuthentication; + +// Starts the authentication process. Set |attemptSSO| to try single sign-on. +// If |attemptSSO| is true, try to authenticate with the Google+ app, if +// installed. If false, always use Google+ via Chrome or Mobile Safari for +// authentication. The delegate will be called at the end of this process. +- (void)authenticate; + +// This method should be called from your |UIApplicationDelegate|'s +// |application:openURL:sourceApplication:annotation|. Returns |YES| if +// |GPPSignIn| handled this URL. +// Also see |handleURL:sourceApplication:annotation:| in |GPPURLHandler|. +- (BOOL)handleURL:(NSURL *)url + sourceApplication:(NSString *)sourceApplication + annotation:(id)annotation; + +// Removes the OAuth 2.0 token from the keychain. +- (void)signOut; + +// Disconnects the user from the app and revokes previous authentication. +// If the operation succeeds, the OAuth 2.0 token is also removed from keychain. +// The token is needed to disconnect so do not call |signOut| if |disconnect| is +// to be called. +- (void)disconnect; + +@end diff --git a/iphone/Maps/GooglePlusSDK/GPPSignInButton.h b/iphone/Maps/GooglePlusSDK/GPPSignInButton.h new file mode 100644 index 0000000000..ea263f8d7c --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/GPPSignInButton.h @@ -0,0 +1,44 @@ +// +// GPPSignInButton.h +// Google+ iOS SDK +// +// Copyright 2012 Google Inc. +// +// Use of this SDK is subject to the Google+ Platform Terms of Service: +// https://developers.google.com/+/terms +// + +#import + +// The various layout styles supported by the GPPSignInButton. +// The minmum size of the button depends on the language used for text. +// The following dimensions (in points) fit for all languages: +// kGPPSignInButtonStyleStandard: 226 x 48 +// kGPPSignInButtonStyleWide: 308 x 48 +// kGPPSignInButtonStyleIconOnly: 46 x 48 (no text, fixed size) +typedef enum { + kGPPSignInButtonStyleStandard = 0, + kGPPSignInButtonStyleWide = 1, + kGPPSignInButtonStyleIconOnly = 2 +} GPPSignInButtonStyle; + +// The various color schemes supported by the GPPSignInButton. +typedef enum { + kGPPSignInButtonColorSchemeDark = 0, + kGPPSignInButtonColorSchemeLight = 1 +} GPPSignInButtonColorScheme; + +// This class provides the Google+ sign-in button. You can instantiate this +// class programmatically or from a NIB file. You should set up the +// |GPPSignIn| shared instance with your client ID and any additional scopes, +// implement the delegate methods for |GPPSignIn|, and add this button to your +// view hierarchy. +@interface GPPSignInButton : UIButton + +// Sets the sign-in button layout style. The default style is standard. +- (void)setStyle:(GPPSignInButtonStyle)style; + +// Sets the sign-in button color scheme. The default scheme is dark. +- (void)setColorScheme:(GPPSignInButtonColorScheme)colorScheme; + +@end diff --git a/iphone/Maps/GooglePlusSDK/GPPURLHandler.h b/iphone/Maps/GooglePlusSDK/GPPURLHandler.h new file mode 100644 index 0000000000..0e45390f56 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/GPPURLHandler.h @@ -0,0 +1,25 @@ +// +// GPPURLHandler.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 +// + +#import + +@interface GPPURLHandler : NSObject + +// Calls |handleURL:sourceApplication:annotation:| for +// |[GPPSignIn sharedInstance]|, |[GPPShare sharedInstance]|, and +// |GPPDeepLink|, and returns |YES| if any of them handles the URL. +// This method can be called from your |UIApplicationDelegate|'s +// |application:openURL:sourceApplication:annotation| instead of calling +// those methods individually. ++ (BOOL)handleURL:(NSURL *)url + sourceApplication:(NSString *)sourceApplication + annotation:(id)annotation; + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLBase64.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLBase64.h new file mode 100644 index 0000000000..fd0a05182d --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLBase64.h @@ -0,0 +1,25 @@ +/* Copyright (c) 2012 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +NSData *GTLDecodeBase64(NSString *base64Str); +NSString *GTLEncodeBase64(NSData *data); + +// "Web-safe" encoding substitutes - and _ for + and / in the encoding table, +// per http://www.ietf.org/rfc/rfc4648.txt section 5. + +NSData *GTLDecodeWebSafeBase64(NSString *base64Str); +NSString *GTLEncodeWebSafeBase64(NSData *data); diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLBase64.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLBase64.m new file mode 100644 index 0000000000..e6c0362715 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLBase64.m @@ -0,0 +1,139 @@ +/* Copyright (c) 2012 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "GTLBase64.h" + +// Based on Cyrus Najmabadi's elegent little encoder and decoder from +// http://www.cocoadev.com/index.pl?BaseSixtyFour + +static char gStandardEncodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static char gWebSafeEncodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + +#pragma mark Encode + +static NSString *EncodeBase64StringCommon(NSData *data, const char *table) { + if (data == nil) return nil; + + const uint8_t* input = [data bytes]; + NSUInteger length = [data length]; + + NSUInteger bufferSize = ((length + 2) / 3) * 4; + NSMutableData* buffer = [NSMutableData dataWithLength:bufferSize]; + + int8_t *output = [buffer mutableBytes]; + + for (NSUInteger i = 0; i < length; i += 3) { + NSUInteger value = 0; + for (NSUInteger j = i; j < (i + 3); j++) { + value <<= 8; + + if (j < length) { + value |= (0xFF & input[j]); + } + } + + NSInteger idx = (i / 3) * 4; + output[idx + 0] = table[(value >> 18) & 0x3F]; + output[idx + 1] = table[(value >> 12) & 0x3F]; + output[idx + 2] = (i + 1) < length ? table[(value >> 6) & 0x3F] : '='; + output[idx + 3] = (i + 2) < length ? table[(value >> 0) & 0x3F] : '='; + } + + NSString *result = [[[NSString alloc] initWithData:buffer + encoding:NSASCIIStringEncoding] autorelease]; + return result; +} + +NSString *GTLEncodeBase64(NSData *data) { + return EncodeBase64StringCommon(data, gStandardEncodingTable); +} + +NSString *GTLEncodeWebSafeBase64(NSData *data) { + return EncodeBase64StringCommon(data, gWebSafeEncodingTable); +} + +#pragma mark Decode + +static void CreateDecodingTable(const char *encodingTable, + size_t encodingTableSize, char *decodingTable) { + memset(decodingTable, 0, 128); + for (unsigned int i = 0; i < encodingTableSize; i++) { + decodingTable[(unsigned int) encodingTable[i]] = (char)i; + } +} + +static NSData *DecodeBase64StringCommon(NSString *base64Str, + char *decodingTable) { + // The input string should be plain ASCII + const char *cString = [base64Str cStringUsingEncoding:NSASCIIStringEncoding]; + if (cString == nil) return nil; + + NSInteger inputLength = (NSInteger)strlen(cString); + if (inputLength % 4 != 0) return nil; + 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) { + 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; +} + +NSData *GTLDecodeBase64(NSString *base64Str) { + static char decodingTable[128]; + static BOOL hasInited = NO; + + if (!hasInited) { + CreateDecodingTable(gStandardEncodingTable, sizeof(gStandardEncodingTable), + decodingTable); + hasInited = YES; + } + return DecodeBase64StringCommon(base64Str, decodingTable); +} + +NSData *GTLDecodeWebSafeBase64(NSString *base64Str) { + static char decodingTable[128]; + static BOOL hasInited = NO; + + if (!hasInited) { + CreateDecodingTable(gWebSafeEncodingTable, sizeof(gWebSafeEncodingTable), + decodingTable); + hasInited = YES; + } + return DecodeBase64StringCommon(base64Str, decodingTable); +} diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLBatchQuery.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLBatchQuery.h new file mode 100644 index 0000000000..5edf17907e --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLBatchQuery.h @@ -0,0 +1,52 @@ +/* Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLBatchQuery.h +// + +// Batch query documentation: +// https://code.google.com/p/google-api-objectivec-client/wiki/Introduction#Batch_Operations + +#import "GTLQuery.h" + +@interface GTLBatchQuery : NSObject { + @private + NSMutableArray *queries_; + NSMutableDictionary *requestIDMap_; + BOOL skipAuthorization_; + NSDictionary *additionalHTTPHeaders_; +} + +// Queries included in this batch. Each query should have a unique requestID. +@property (retain) NSArray *queries; + +// Clients may set this to YES to disallow authorization. Defaults to NO. +@property (assign) BOOL shouldSkipAuthorization; + +// Any additional HTTP headers for this batch. +// +// These headers override the same keys from the service object's +// additionalHTTPHeaders. +@property (copy) NSDictionary *additionalHTTPHeaders; + ++ (id)batchQuery; ++ (id)batchQueryWithQueries:(NSArray *)array; + +- (void)addQuery:(GTLQuery *)query GTL_NONNULL((1)); + +- (GTLQuery *)queryForRequestID:(NSString *)requestID GTL_NONNULL((1)); + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLBatchQuery.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLBatchQuery.m new file mode 100644 index 0000000000..f62eaca1f5 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLBatchQuery.m @@ -0,0 +1,133 @@ +/* Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLBatchQuery.m +// + +#import "GTLBatchQuery.h" + +@implementation GTLBatchQuery + +@synthesize shouldSkipAuthorization = skipAuthorization_, + additionalHTTPHeaders = additionalHTTPHeaders_; + ++ (id)batchQuery { + GTLBatchQuery *obj = [[[self alloc] init] autorelease]; + return obj; +} + ++ (id)batchQueryWithQueries:(NSArray *)queries { + GTLBatchQuery *obj = [self batchQuery]; + obj.queries = queries; + return obj; +} + +- (id)copyWithZone:(NSZone *)zone { + // Deep copy the list of queries + NSArray *copiesOfQueries = [[[NSArray alloc] initWithArray:self.queries + copyItems:YES] autorelease]; + GTLBatchQuery *newBatch = [[[self class] allocWithZone:zone] init]; + newBatch.queries = copiesOfQueries; + newBatch.shouldSkipAuthorization = self.shouldSkipAuthorization; + newBatch.additionalHTTPHeaders = self.additionalHTTPHeaders; + return newBatch; +} + +- (void)dealloc { + [queries_ release]; + [additionalHTTPHeaders_ release]; + [requestIDMap_ release]; + + [super dealloc]; +} + +- (NSString *)description { + NSArray *queries = self.queries; + NSArray *methodNames = [queries valueForKey:@"methodName"]; + NSArray *dedupedNames = [[NSSet setWithArray:methodNames] allObjects]; + NSString *namesStr = [dedupedNames componentsJoinedByString:@","]; + + return [NSString stringWithFormat:@"%@ %p (queries:%lu methods:%@)", + [self class], self, (unsigned long) [queries count], namesStr]; +} + +#pragma mark - + +- (BOOL)isBatchQuery { + return YES; +} + +- (GTLUploadParameters *)uploadParameters { + // File upload is not supported for batches + return nil; +} + +- (void)executionDidStop { + NSArray *queries = self.queries; + [queries makeObjectsPerformSelector:@selector(executionDidStop)]; +} + +- (GTLQuery *)queryForRequestID:(NSString *)requestID { + GTLQuery *result = [requestIDMap_ objectForKey:requestID]; + if (result) return result; + + // We've not before tried to look up a query, or the map is stale + [requestIDMap_ release]; + requestIDMap_ = [[NSMutableDictionary alloc] init]; + + for (GTLQuery *query in queries_) { + [requestIDMap_ setObject:query forKey:query.requestID]; + } + + result = [requestIDMap_ objectForKey:requestID]; + return result; +} + +#pragma mark - + +- (void)setQueries:(NSArray *)array { +#if DEBUG + for (id obj in array) { + GTLQuery *query = obj; + GTL_DEBUG_ASSERT([query isKindOfClass:[GTLQuery class]], + @"unexpected query class: %@", [obj class]); + GTL_DEBUG_ASSERT(query.uploadParameters == nil, + @"batch may not contain upload: %@", query); + } +#endif + + [queries_ autorelease]; + queries_ = [array mutableCopy]; +} + +- (NSArray *)queries { + return queries_; +} + +- (void)addQuery:(GTLQuery *)query { + GTL_DEBUG_ASSERT([query isKindOfClass:[GTLQuery class]], + @"unexpected query class: %@", [query class]); + GTL_DEBUG_ASSERT(query.uploadParameters == nil, + @"batch may not contain upload: %@", query); + + if (queries_ == nil) { + queries_ = [[NSMutableArray alloc] init]; + } + + [queries_ addObject:query]; +} + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLBatchResult.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLBatchResult.h new file mode 100644 index 0000000000..9675aaf741 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLBatchResult.h @@ -0,0 +1,58 @@ +/* Copyright (c) 2011 Google Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +// +// GTLBatchResult.h +// + +#import "GTLObject.h" + +@interface GTLBatchResult : GTLObject { + @private + NSMutableDictionary *successes_; + NSMutableDictionary *failures_; +} + +// Dictionaries of results for all queries in the batch +// +// Dictionary keys are requestID strings; objects are results or +// GTLErrorObjects. +// +// For successes with no returned object (such as from delete operations), +// the object for the dictionary entry is NSNull. +// +// +// The original query for each result is available from the service ticket, +// for example +// +// NSDictionary *successes = batchResults.successes; +// for (NSString *requestID in successes) { +// GTLObject *obj = [successes objectForKey:requestID]; +// GTLQuery *query = [ticket queryForRequestID:requestID]; +// NSLog(@"Query %@ returned object %@", query, obj); +// } +// +// NSDictionary *failures = batchResults.failures; +// for (NSString *requestID in failures) { +// GTLErrorObject *errorObj = [failures objectForKey:requestID]; +// GTLQuery *query = [ticket queryForRequestID:requestID]; +// NSLog(@"Query %@ failed with error %@", query, errorObj); +// } +// + +@property (retain) NSMutableDictionary *successes; +@property (retain) NSMutableDictionary *failures; + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLBatchResult.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLBatchResult.m new file mode 100644 index 0000000000..f17748d5b7 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLBatchResult.m @@ -0,0 +1,92 @@ +/* Copyright (c) 2011 Google Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +// +// GTLBatchResult.m +// + +#import "GTLBatchResult.h" + +#import "GTLErrorObject.h" + +@implementation GTLBatchResult + +@synthesize successes = successes_, + failures = failures_; + +- (id)copyWithZone:(NSZone *)zone { + GTLBatchResult* newObject = [super copyWithZone:zone]; + newObject.successes = [[self.successes mutableCopyWithZone:zone] autorelease]; + newObject.failures = [[self.failures mutableCopyWithZone:zone] autorelease]; + return newObject; +} + +- (void)dealloc { + [successes_ release]; + [failures_ release]; + + [super dealloc]; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"%@ %p (successes:%lu failures:%lu)", + [self class], self, + (unsigned long) [self.successes count], + (unsigned long) [self.failures count]]; +} + +#pragma mark - + +- (void)createItemsWithClassMap:(NSDictionary *)batchClassMap { + // This is called by GTLObject objectForJSON:defaultClass: + // JSON is defined to be a dictionary, but for batch results, it really + // is any array. + id json = self.JSON; + GTL_DEBUG_ASSERT([json isKindOfClass:[NSArray class]], + @"didn't get an array for the batch results"); + NSArray *jsonArray = json; + + NSMutableDictionary *successes = [NSMutableDictionary dictionary]; + NSMutableDictionary *failures = [NSMutableDictionary dictionary]; + + for (NSMutableDictionary *rpcResponse in jsonArray) { + NSString *responseID = [rpcResponse objectForKey:@"id"]; + + NSMutableDictionary *errorJSON = [rpcResponse objectForKey:@"error"]; + if (errorJSON) { + GTLErrorObject *errorObject = [GTLErrorObject objectWithJSON:errorJSON]; + [failures setValue:errorObject forKey:responseID]; + } else { + NSMutableDictionary *resultJSON = [rpcResponse objectForKey:@"result"]; + + NSDictionary *surrogates = self.surrogates; + Class defaultClass = [batchClassMap objectForKey:responseID]; + + id resultObject = [[self class] objectForJSON:resultJSON + defaultClass:defaultClass + surrogates:surrogates + batchClassMap:nil]; + if (resultObject == nil) { + // methods like delete return no object + resultObject = [NSNull null]; + } + [successes setValue:resultObject forKey:responseID]; + } + } + self.successes = successes; + self.failures = failures; +} + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLDateTime.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLDateTime.h new file mode 100644 index 0000000000..f6b1ffb3cf --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLDateTime.h @@ -0,0 +1,60 @@ +/* Copyright (c) 2011 Google Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +// +// GTLDateTime.h +// +// This is an immutable class representing a date and optionally a +// time with time zone. +// + +#import +#import "GTLDefines.h" + +@interface GTLDateTime : NSObject { + NSDateComponents *dateComponents_; + NSInteger milliseconds_; // This is only for the fraction of a second 0-999 + NSInteger offsetSeconds_; // may be NSUndefinedDateComponent + BOOL isUniversalTime_; // preserves "Z" + NSTimeZone *timeZone_; // specific time zone by name, if known +} + ++ (GTLDateTime *)dateTimeWithRFC3339String:(NSString *)str; + +// timeZone may be nil if the time zone is not known. ++ (GTLDateTime *)dateTimeWithDate:(NSDate *)date timeZone:(NSTimeZone *)tz; + +// Use this method to make a dateTime for an all-day event (date only, so +// hasTime is NO.) ++ (GTLDateTime *)dateTimeForAllDayWithDate:(NSDate *)date; + ++ (GTLDateTime *)dateTimeWithDateComponents:(NSDateComponents *)date; + +@property (nonatomic, readonly) NSDate *date; +@property (nonatomic, readonly) NSCalendar *calendar; + +@property (nonatomic, readonly) NSString *RFC3339String; +@property (nonatomic, readonly) NSString *stringValue; // same as RFC3339String + +@property (nonatomic, readonly, retain) NSTimeZone *timeZone; +@property (nonatomic, readonly, copy) NSDateComponents *dateComponents; +@property (nonatomic, readonly) NSInteger milliseconds; // This is only for the fraction of a second 0-999 + +@property (nonatomic, readonly) BOOL hasTime; +@property (nonatomic, readonly) NSInteger offsetSeconds; +@property (nonatomic, readonly, getter=isUniversalTime) BOOL universalTime; + + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLDateTime.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLDateTime.m new file mode 100644 index 0000000000..a55b049a4c --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLDateTime.m @@ -0,0 +1,471 @@ +/* Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLDateTime.m +// + +#import "GTLDateTime.h" + +@interface GTLDateTime () + +- (void)setFromDate:(NSDate *)date timeZone:(NSTimeZone *)tz; +- (void)setFromRFC3339String:(NSString *)str; + +@property (nonatomic, retain, readwrite) NSTimeZone *timeZone; +@property (nonatomic, copy, readwrite) NSDateComponents *dateComponents; +@property (nonatomic, assign, readwrite) NSInteger milliseconds; + +@property (nonatomic, assign, readwrite) BOOL hasTime; +@property (nonatomic, assign, readwrite) NSInteger offsetSeconds; +@property (nonatomic, assign, getter=isUniversalTime, readwrite) BOOL universalTime; + +@end + +static NSCharacterSet *gDashSet = nil; +static NSCharacterSet *gTSet = nil; +static NSCharacterSet *gColonSet = nil; +static NSCharacterSet *gPlusMinusZSet = nil; +static NSMutableDictionary *gCalendarsForTimeZones = nil; + +@implementation GTLDateTime + +// A note about milliseconds_: +// RFC 3339 has support for fractions of a second. NSDateComponents is all +// NSInteger based, so it can't handle a fraction of a second. NSDate is +// built on NSTimeInterval so it has sub-millisecond precision. GTL takes +// the compromise of supporting the RFC's optional fractional second support +// by maintaining a number of milliseconds past what fits in the +// NSDateComponents. The parsing and string conversions will include +// 3 decimal digits (hence milliseconds). When going to a string, the decimal +// digits are only included if the milliseconds are non zero. + +@dynamic date; +@dynamic calendar; +@dynamic RFC3339String; +@dynamic stringValue; +@dynamic timeZone; +@dynamic hasTime; + +@synthesize dateComponents = dateComponents_, + milliseconds = milliseconds_, + offsetSeconds = offsetSeconds_, + universalTime = isUniversalTime_; + ++ (void)initialize { + // Note that initialize is guaranteed by the runtime to be called in a + // thread-safe manner. + if (gDashSet == nil) { + gDashSet = [[NSCharacterSet characterSetWithCharactersInString:@"-"] retain]; + gTSet = [[NSCharacterSet characterSetWithCharactersInString:@"Tt "] retain]; + gColonSet = [[NSCharacterSet characterSetWithCharactersInString:@":"] retain]; + gPlusMinusZSet = [[NSCharacterSet characterSetWithCharactersInString:@"+-zZ"] retain]; + + gCalendarsForTimeZones = [[NSMutableDictionary alloc] init]; + } +} + ++ (GTLDateTime *)dateTimeWithRFC3339String:(NSString *)str { + if (str == nil) return nil; + + GTLDateTime *result = [[[self alloc] init] autorelease]; + [result setFromRFC3339String:str]; + return result; +} + ++ (GTLDateTime *)dateTimeWithDate:(NSDate *)date timeZone:(NSTimeZone *)tz { + if (date == nil) return nil; + + GTLDateTime *result = [[[self alloc] init] autorelease]; + [result setFromDate:date timeZone:tz]; + return result; +} + ++ (GTLDateTime *)dateTimeForAllDayWithDate:(NSDate *)date { + if (date == nil) return nil; + + GTLDateTime *result = [[[self alloc] init] autorelease]; + [result setFromDate:date timeZone:nil]; + result.hasTime = NO; + return result; +} + ++ (GTLDateTime *)dateTimeWithDateComponents:(NSDateComponents *)components { + NSCalendar *cal = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease]; + NSDate *date = [cal dateFromComponents:components]; +#if GTL_IPHONE + NSTimeZone *tz = [components timeZone]; +#else + // NSDateComponents added timeZone: in Mac OS X 10.7. + NSTimeZone *tz = nil; + if ([components respondsToSelector:@selector(timeZone)]) { + tz = [components timeZone]; + } +#endif + return [self dateTimeWithDate:date timeZone:tz]; +} + +- (void)dealloc { + [dateComponents_ release]; + [timeZone_ release]; + [super dealloc]; +} + +- (id)copyWithZone:(NSZone *)zone { + // Object is immutable + return [self retain]; +} + +// until NSDateComponent implements isEqual, we'll use this +- (BOOL)doesDateComponents:(NSDateComponents *)dc1 + equalDateComponents:(NSDateComponents *)dc2 { + + return [dc1 era] == [dc2 era] + && [dc1 year] == [dc2 year] + && [dc1 month] == [dc2 month] + && [dc1 day] == [dc2 day] + && [dc1 hour] == [dc2 hour] + && [dc1 minute] == [dc2 minute] + && [dc1 second] == [dc2 second] + && [dc1 week] == [dc2 week] + && [dc1 weekday] == [dc2 weekday] + && [dc1 weekdayOrdinal] == [dc2 weekdayOrdinal]; +} + +- (BOOL)isEqual:(GTLDateTime *)other { + + if (self == other) return YES; + if (![other isKindOfClass:[GTLDateTime class]]) return NO; + + BOOL areDateComponentsEqual = [self doesDateComponents:self.dateComponents + equalDateComponents:other.dateComponents]; + NSTimeZone *tz1 = self.timeZone; + NSTimeZone *tz2 = other.timeZone; + BOOL areTimeZonesEqual = (tz1 == tz2 || (tz2 && [tz1 isEqual:tz2])); + + return self.offsetSeconds == other.offsetSeconds + && self.isUniversalTime == other.isUniversalTime + && self.milliseconds == other.milliseconds + && areDateComponentsEqual + && areTimeZonesEqual; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"%@ %p: {%@}", + [self class], self, self.RFC3339String]; +} + +- (NSTimeZone *)timeZone { + if (timeZone_) { + return timeZone_; + } + + if (self.isUniversalTime) { + NSTimeZone *ztz = [NSTimeZone timeZoneWithName:@"Universal"]; + return ztz; + } + + NSInteger offsetSeconds = self.offsetSeconds; + + if (offsetSeconds != NSUndefinedDateComponent) { + NSTimeZone *tz = [NSTimeZone timeZoneForSecondsFromGMT:offsetSeconds]; + return tz; + } + return nil; +} + +- (void)setTimeZone:(NSTimeZone *)timeZone { + [timeZone_ release]; + timeZone_ = [timeZone retain]; + + if (timeZone) { + NSInteger offsetSeconds = [timeZone secondsFromGMTForDate:self.date]; + self.offsetSeconds = offsetSeconds; + } else { + self.offsetSeconds = NSUndefinedDateComponent; + } +} + +- (NSCalendar *)calendarForTimeZone:(NSTimeZone *)tz { + NSCalendar *cal = nil; + @synchronized(gCalendarsForTimeZones) { + id tzKey = (tz ? tz : [NSNull null]); + cal = [gCalendarsForTimeZones objectForKey:tzKey]; + if (cal == nil) { + cal = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease]; + if (tz) { + [cal setTimeZone:tz]; + } + [gCalendarsForTimeZones setObject:cal forKey:tzKey]; + } + } + return cal; +} + +- (NSCalendar *)calendar { + NSTimeZone *tz = self.timeZone; + return [self calendarForTimeZone:tz]; +} + +- (NSDate *)date { + NSDateComponents *dateComponents = self.dateComponents; + NSTimeInterval extraMillisecondsAsSeconds = 0.0; + NSCalendar *cal; + + if (!self.hasTime) { + // We're not keeping track of a time, but NSDate always is based on + // an absolute time. We want to avoid returning an NSDate where the + // calendar date appears different from what was used to create our + // date-time object. + // + // We'll make a copy of the date components, setting the time on our + // copy to noon GMT, since that ensures the date renders correctly for + // any time zone. + NSDateComponents *noonDateComponents = [[dateComponents copy] autorelease]; + [noonDateComponents setHour:12]; + [noonDateComponents setMinute:0]; + [noonDateComponents setSecond:0]; + dateComponents = noonDateComponents; + + NSTimeZone *gmt = [NSTimeZone timeZoneWithName:@"Universal"]; + cal = [self calendarForTimeZone:gmt]; + } else { + cal = self.calendar; + + // Add in the fractional seconds that don't fit into NSDateComponents. + extraMillisecondsAsSeconds = ((NSTimeInterval)self.milliseconds) / 1000.0; + } + + NSDate *date = [cal dateFromComponents:dateComponents]; + + // Add in any milliseconds that didn't fit into the dateComponents. + if (extraMillisecondsAsSeconds > 0.0) { +#if GTL_IPHONE || (MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5) + date = [date dateByAddingTimeInterval:extraMillisecondsAsSeconds]; +#else + date = [date addTimeInterval:extraMillisecondsAsSeconds]; +#endif + } + + return date; +} + +- (NSString *)stringValue { + return self.RFC3339String; +} + +- (NSString *)RFC3339String { + NSDateComponents *dateComponents = self.dateComponents; + NSInteger offset = self.offsetSeconds; + + NSString *timeString = @""; // timeString like "T15:10:46-08:00" + + if (self.hasTime) { + + NSString *timeOffsetString; // timeOffsetString like "-08:00" + + if (self.isUniversalTime) { + timeOffsetString = @"Z"; + } else if (offset == NSUndefinedDateComponent) { + // unknown offset is rendered as -00:00 per + // http://www.ietf.org/rfc/rfc3339.txt section 4.3 + timeOffsetString = @"-00:00"; + } else { + NSString *sign = @"+"; + if (offset < 0) { + sign = @"-"; + offset = -offset; + } + timeOffsetString = [NSString stringWithFormat:@"%@%02ld:%02ld", + sign, (long)(offset/(60*60)) % 24, (long)(offset / 60) % 60]; + } + + NSString *fractionalSecondsString = @""; + if (self.milliseconds > 0.0) { + fractionalSecondsString = [NSString stringWithFormat:@".%03ld", (long)self.milliseconds]; + } + + timeString = [NSString stringWithFormat:@"T%02ld:%02ld:%02ld%@%@", + (long)[dateComponents hour], (long)[dateComponents minute], + (long)[dateComponents second], fractionalSecondsString, timeOffsetString]; + } + + // full dateString like "2006-11-17T15:10:46-08:00" + NSString *dateString = [NSString stringWithFormat:@"%04ld-%02ld-%02ld%@", + (long)[dateComponents year], (long)[dateComponents month], + (long)[dateComponents day], timeString]; + + return dateString; +} + +- (void)setFromDate:(NSDate *)date timeZone:(NSTimeZone *)tz { + NSCalendar *cal = [self calendarForTimeZone:tz]; + + NSUInteger const kComponentBits = (NSYearCalendarUnit | NSMonthCalendarUnit + | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit + | NSSecondCalendarUnit); + + NSDateComponents *components = [cal components:kComponentBits fromDate:date]; + self.dateComponents = components; + + // Extract the fractional seconds. + NSTimeInterval asTimeInterval = [date timeIntervalSince1970]; + NSTimeInterval worker = asTimeInterval - trunc(asTimeInterval); + self.milliseconds = (NSInteger)round(worker * 1000.0); + + self.universalTime = NO; + + NSInteger offset = NSUndefinedDateComponent; + + if (tz) { + offset = [tz secondsFromGMTForDate:date]; + + if (offset == 0 && [tz isEqualToTimeZone:[NSTimeZone timeZoneWithName:@"Universal"]]) { + self.universalTime = YES; + } + } + self.offsetSeconds = offset; + + // though offset seconds are authoritative, we'll retain the time zone + // since we can't regenerate it reliably from just the offset + timeZone_ = [tz retain]; +} + +- (void)setFromRFC3339String:(NSString *)str { + + NSInteger year = NSUndefinedDateComponent; + NSInteger month = NSUndefinedDateComponent; + NSInteger day = NSUndefinedDateComponent; + NSInteger hour = NSUndefinedDateComponent; + NSInteger minute = NSUndefinedDateComponent; + NSInteger sec = NSUndefinedDateComponent; + NSInteger milliseconds = 0; + double secDouble = -1.0; + NSString* sign = nil; + NSInteger offsetHour = 0; + NSInteger offsetMinute = 0; + + if ([str length] > 0) { + NSScanner* scanner = [NSScanner scannerWithString:str]; + // There should be no whitespace, so no skip characters. + [scanner setCharactersToBeSkipped:nil]; + + // for example, scan 2006-11-17T15:10:46-08:00 + // or 2006-11-17T15:10:46Z + if (// yyyy-mm-dd + [scanner scanInteger:&year] && + [scanner scanCharactersFromSet:gDashSet intoString:NULL] && + [scanner scanInteger:&month] && + [scanner scanCharactersFromSet:gDashSet intoString:NULL] && + [scanner scanInteger:&day] && + // Thh:mm:ss + [scanner scanCharactersFromSet:gTSet intoString:NULL] && + [scanner scanInteger:&hour] && + [scanner scanCharactersFromSet:gColonSet intoString:NULL] && + [scanner scanInteger:&minute] && + [scanner scanCharactersFromSet:gColonSet intoString:NULL] && + [scanner scanDouble:&secDouble]) { + + // At this point we got secDouble, pull it apart. + sec = (NSInteger)secDouble; + double worker = secDouble - ((double)sec); + milliseconds = (NSInteger)round(worker * 1000.0); + + // Finish parsing, now the offset info. + if (// Z or +hh:mm + [scanner scanCharactersFromSet:gPlusMinusZSet intoString:&sign] && + [scanner scanInteger:&offsetHour] && + [scanner scanCharactersFromSet:gColonSet intoString:NULL] && + [scanner scanInteger:&offsetMinute]) { + } + } + } + + NSDateComponents *dateComponents = [[[NSDateComponents alloc] init] autorelease]; + [dateComponents setYear:year]; + [dateComponents setMonth:month]; + [dateComponents setDay:day]; + [dateComponents setHour:hour]; + [dateComponents setMinute:minute]; + [dateComponents setSecond:sec]; + + self.dateComponents = dateComponents; + self.milliseconds = milliseconds; + + // determine the offset, like from Z, or -08:00:00.0 + + self.timeZone = nil; + + NSInteger totalOffset = NSUndefinedDateComponent; + self.universalTime = NO; + + if ([sign caseInsensitiveCompare:@"Z"] == NSOrderedSame) { + + self.universalTime = YES; + totalOffset = 0; + + } else if (sign != nil) { + + totalOffset = (60 * offsetMinute) + (60 * 60 * offsetHour); + + if ([sign isEqual:@"-"]) { + + if (totalOffset == 0) { + // special case: offset of -0.00 means undefined offset + totalOffset = NSUndefinedDateComponent; + } else { + totalOffset *= -1; + } + } + } + + self.offsetSeconds = totalOffset; +} + +- (BOOL)hasTime { + NSDateComponents *dateComponents = self.dateComponents; + + BOOL hasTime = ([dateComponents hour] != NSUndefinedDateComponent + && [dateComponents minute] != NSUndefinedDateComponent); + + return hasTime; +} + +- (void)setHasTime:(BOOL)shouldHaveTime { + + // we'll set time values to zero or NSUndefinedDateComponent as appropriate + BOOL hadTime = self.hasTime; + + if (shouldHaveTime && !hadTime) { + [dateComponents_ setHour:0]; + [dateComponents_ setMinute:0]; + [dateComponents_ setSecond:0]; + milliseconds_ = 0; + offsetSeconds_ = NSUndefinedDateComponent; + isUniversalTime_ = NO; + + } else if (hadTime && !shouldHaveTime) { + [dateComponents_ setHour:NSUndefinedDateComponent]; + [dateComponents_ setMinute:NSUndefinedDateComponent]; + [dateComponents_ setSecond:NSUndefinedDateComponent]; + milliseconds_ = 0; + offsetSeconds_ = NSUndefinedDateComponent; + isUniversalTime_ = NO; + self.timeZone = nil; + } +} + + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLDefines.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLDefines.h new file mode 100644 index 0000000000..b12eb9eb62 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLDefines.h @@ -0,0 +1,144 @@ +/* Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLDefines.h +// + +// Ensure Apple's conditionals we depend on are defined. +#import +#import + +// +// The developer may choose to define these in the project: +// +// #define GTL_TARGET_NAMESPACE Xxx // preface all GTL class names with Xxx (recommended for building plug-ins) +// #define GTL_FOUNDATION_ONLY 1 // builds without AppKit or Carbon (default for iPhone builds) +// #define STRIP_GTM_FETCH_LOGGING 1 // omit http logging code (default for iPhone release builds) +// +// Mac developers may find GTL_SIMPLE_DESCRIPTIONS and STRIP_GTM_FETCH_LOGGING useful for +// reducing code size. +// + +// Define later OS versions when building on earlier versions +#ifdef MAC_OS_X_VERSION_10_0 + #ifndef MAC_OS_X_VERSION_10_6 + #define MAC_OS_X_VERSION_10_6 1060 + #endif +#endif + + +#ifdef GTL_TARGET_NAMESPACE +// prefix all GTL class names with GTL_TARGET_NAMESPACE for this target + #import "GTLTargetNamespace.h" +#endif + +// Provide a common definition for externing constants/functions +#if defined(__cplusplus) + #define GTL_EXTERN extern "C" +#else + #define GTL_EXTERN extern +#endif + +#if TARGET_OS_IPHONE // iPhone SDK + + #define GTL_IPHONE 1 + +#endif + +#if GTL_IPHONE + + #define GTL_FOUNDATION_ONLY 1 + +#endif + +// +// GTL_ASSERT is like NSAssert, but takes a variable number of arguments: +// +// GTL_ASSERT(condition, @"Problem in argument %@", argStr); +// +// GTL_DEBUG_ASSERT is similar, but compiles in only for debug builds +// + +#ifndef GTL_ASSERT + // we directly invoke the NSAssert handler so we can pass on the varargs + #if !defined(NS_BLOCK_ASSERTIONS) + #define GTL_ASSERT(condition, ...) \ + do { \ + if (!(condition)) { \ + [[NSAssertionHandler currentHandler] \ + handleFailureInFunction:[NSString stringWithUTF8String:__PRETTY_FUNCTION__] \ + file:[NSString stringWithUTF8String:__FILE__] \ + lineNumber:__LINE__ \ + description:__VA_ARGS__]; \ + } \ + } while(0) + #else + #define GTL_ASSERT(condition, ...) do { } while (0) + #endif // !defined(NS_BLOCK_ASSERTIONS) +#endif // GTL_ASSERT + +#ifndef GTL_DEBUG_ASSERT + #if DEBUG + #define GTL_DEBUG_ASSERT(condition, ...) GTL_ASSERT(condition, __VA_ARGS__) + #else + #define GTL_DEBUG_ASSERT(condition, ...) do { } while (0) + #endif +#endif + +#ifndef GTL_DEBUG_LOG + #if DEBUG + #define GTL_DEBUG_LOG(...) NSLog(__VA_ARGS__) + #else + #define GTL_DEBUG_LOG(...) do { } while (0) + #endif +#endif + +#ifndef STRIP_GTM_FETCH_LOGGING + #if GTL_IPHONE && !DEBUG + #define STRIP_GTM_FETCH_LOGGING 1 + #else + #define STRIP_GTM_FETCH_LOGGING 0 + #endif +#endif + +// Some support for advanced clang static analysis functionality +// See http://clang-analyzer.llvm.org/annotations.html +#ifndef __has_feature // Optional. + #define __has_feature(x) 0 // Compatibility with non-clang compilers. +#endif +#ifndef NS_RETURNS_NOT_RETAINED + #if __has_feature(attribute_ns_returns_not_retained) + #define NS_RETURNS_NOT_RETAINED __attribute__((ns_returns_not_retained)) + #else + #define NS_RETURNS_NOT_RETAINED + #endif +#endif + +#ifndef __has_attribute + #define __has_attribute(x) 0 +#endif + +#if 1 + // We will start using nonnull declarations once the static analyzer seems + // to support it without false positives. + #define GTL_NONNULL(x) +#else + #if __has_attribute(nonnull) + #define GTL_NONNULL(x) __attribute__((nonnull x)) + #else + #define GTL_NONNULL(x) + #endif +#endif diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLErrorObject.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLErrorObject.h new file mode 100644 index 0000000000..c2ec67db3e --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLErrorObject.h @@ -0,0 +1,45 @@ +/* Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLErrorObject.h +// + +#import "GTLObject.h" + +@class GTLErrorObjectData; + +@interface GTLErrorObject : GTLObject +@property (retain) NSNumber *code; +@property (retain) NSString *message; +@property (retain) NSArray *data; // of GTLErrorObjectData + +// Convenience accessor for creating an NSError from a GTLErrorObject. +@property (readonly) NSError *foundationError; + +// Convenience accessor for extracting the GTLErrorObject that was used to +// create an NSError. +// +// Returns nil if the error was not originally from a GTLErrorObject. ++ (GTLErrorObject *)underlyingObjectForError:(NSError *)foundationError; + +@end + +@interface GTLErrorObjectData : GTLObject +@property (retain) NSString *domain; +@property (retain) NSString *reason; +@property (retain) NSString *message; +@property (retain) NSString *location; +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLErrorObject.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLErrorObject.m new file mode 100644 index 0000000000..1fa1023ac2 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLErrorObject.m @@ -0,0 +1,78 @@ +/* Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLErrorObject.m +// + +#import "GTLErrorObject.h" +#import "GTLService.h" + +@implementation GTLErrorObject + +@dynamic code; +@dynamic message; +@dynamic data; + ++ (NSDictionary *)arrayPropertyToClassMap { + NSDictionary *map = [NSDictionary dictionaryWithObject:[GTLErrorObjectData class] + forKey:@"data"]; + return map; +} + +- (NSError *)foundationError { + NSMutableDictionary *userInfo; + + // This structured GTLErrorObject will be available in the error's userInfo + // dictionary + userInfo = [NSMutableDictionary dictionaryWithObject:self + forKey:kGTLStructuredErrorKey]; + + NSString *reasonStr = self.message; + if (reasonStr) { + // We always store an error in the userInfo key "error" + [userInfo setObject:reasonStr + forKey:kGTLServerErrorStringKey]; + + // Store a user-readable "reason" to show up when an error is logged, + // in parentheses like NSError does it + NSString *parenthesized = [NSString stringWithFormat:@"(%@)", reasonStr]; + [userInfo setObject:parenthesized + forKey:NSLocalizedFailureReasonErrorKey]; + } + + NSInteger code = [self.code integerValue]; + NSError *error = [NSError errorWithDomain:kGTLJSONRPCErrorDomain + code:code + userInfo:userInfo]; + return error; +} + ++ (GTLErrorObject *)underlyingObjectForError:(NSError *)foundationError { + NSDictionary *userInfo = [foundationError userInfo]; + GTLErrorObject *errorObj = [userInfo objectForKey:kGTLStructuredErrorKey]; + return errorObj; +} + +@end + +@implementation GTLErrorObjectData +@dynamic domain; +@dynamic reason; +@dynamic message; +@dynamic location; +@end + + diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLFramework.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLFramework.h new file mode 100644 index 0000000000..106f420ef6 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLFramework.h @@ -0,0 +1,35 @@ +/* Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef _GTLFRAMEWORK_H_ +#define _GTLFRAMEWORK_H_ + +#import + +#import "GTLDefines.h" + + +// Returns the version of the framework. Major and minor should +// match the bundle version in the Info.plist file. +// +// Pass NULL to ignore any of the parameters. + +void GTLFrameworkVersion(NSUInteger* major, NSUInteger* minor, NSUInteger* release); + +// Returns the version in @"a.b" or @"a.b.c" format +NSString *GTLFrameworkVersionString(void); + +#endif diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLFramework.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLFramework.m new file mode 100644 index 0000000000..6bfc7f22aa --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLFramework.m @@ -0,0 +1,40 @@ +/* Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "GTLFramework.h" + +void GTLFrameworkVersion(NSUInteger* major, NSUInteger* minor, NSUInteger* release) { + // version 2.0.0 + if (major) *major = 2; + if (minor) *minor = 0; + if (release) *release = 0; +} + +NSString *GTLFrameworkVersionString(void) { + NSUInteger major, minor, release; + NSString *libVersionString; + + GTLFrameworkVersion(&major, &minor, &release); + + // most library releases will have a release value of zero + if (release != 0) { + libVersionString = [NSString stringWithFormat:@"%d.%d.%d", + (int)major, (int)minor, (int)release]; + } else { + libVersionString = [NSString stringWithFormat:@"%d.%d", + (int)major, (int)minor]; + } + return libVersionString; +} diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLJSONParser.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLJSONParser.h new file mode 100644 index 0000000000..d971529586 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLJSONParser.h @@ -0,0 +1,41 @@ +/* Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLJSONParser.h +// + +// This class is a thin wrapper around the JSON parser. It uses +// NSJSONSerialization when available, and SBJSON otherwise. + +#import + +#import "GTLDefines.h" + +@interface GTLJSONParser : NSObject ++ (NSString*)stringWithObject:(id)value + humanReadable:(BOOL)humanReadable + error:(NSError**)error; + ++ (NSData *)dataWithObject:(id)obj + humanReadable:(BOOL)humanReadable + error:(NSError**)error; + ++ (id)objectWithString:(NSString *)jsonStr + error:(NSError **)error; + ++ (id)objectWithData:(NSData *)jsonData + error:(NSError **)error; +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLJSONParser.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLJSONParser.m new file mode 100644 index 0000000000..a089a93df1 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLJSONParser.m @@ -0,0 +1,150 @@ +/* Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLJSONParser.m +// + +#import "GTLJSONParser.h" + +// We can assume NSJSONSerialization is present on Mac OS X 10.7 and iOS 5 +#if !defined(GTL_REQUIRES_NSJSONSERIALIZATION) +#if (!TARGET_OS_IPHONE && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)) || \ + (TARGET_OS_IPHONE && (__IPHONE_OS_VERSION_MIN_REQUIRED >= 50000)) +#define GTL_REQUIRES_NSJSONSERIALIZATION 1 +#endif +#endif + +// If GTMNSJSONSerialization is available, it is used for parsing and +// formatting JSON +#if !GTL_REQUIRES_NSJSONSERIALIZATION +@interface GTMNSJSONSerialization : NSObject ++ (NSData *)dataWithJSONObject:(id)obj options:(NSUInteger)opt error:(NSError **)error; ++ (id)JSONObjectWithData:(NSData *)data options:(NSUInteger)opt error:(NSError **)error; +@end + +// As a fallback, SBJSON is used for parsing and formatting JSON +@interface GTLSBJSON +- (void)setHumanReadable:(BOOL)flag; +- (NSString*)stringWithObject:(id)value error:(NSError**)error; +- (id)objectWithString:(NSString*)jsonrep error:(NSError**)error; +@end +#endif // !GTL_REQUIRES_NSJSONSERIALIZATION + +@implementation GTLJSONParser + +#if DEBUG && !GTL_REQUIRES_NSJSONSERIALIZATION +// When compiling for iOS 4 compatibility, SBJSON must be available ++ (void)load { + Class writer = NSClassFromString(@"SBJsonWriter"); + Class parser = NSClassFromString(@"SBJsonParser"); + Class oldParser = NSClassFromString(@"SBJSON"); + GTL_ASSERT((oldParser != Nil) + || (writer != Nil && parser != Nil), + @"No parsing class found"); +} +#endif // DEBUG && !GTL_REQUIRES_NSJSONSERIALIZATION + ++ (NSString*)stringWithObject:(id)obj + humanReadable:(BOOL)humanReadable + error:(NSError**)error { + NSData *data = [self dataWithObject:obj + humanReadable:humanReadable + error:error]; + if (data) { + NSString *jsonStr = [[[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding] autorelease]; + return jsonStr; + } + return nil; +} + ++ (NSData *)dataWithObject:(id)obj + humanReadable:(BOOL)humanReadable + error:(NSError**)error { + const NSUInteger kOpts = humanReadable ? (1UL << 0) : 0; // NSJSONWritingPrettyPrinted + +#if GTL_REQUIRES_NSJSONSERIALIZATION + NSData *data = [NSJSONSerialization dataWithJSONObject:obj + options:kOpts + error:error]; + return data; +#else + Class serializer = NSClassFromString(@"NSJSONSerialization"); + if (serializer) { + NSData *data = [serializer dataWithJSONObject:obj + options:kOpts + error:error]; + return data; + } else { + Class jsonWriteClass = NSClassFromString(@"SBJsonWriter"); + if (!jsonWriteClass) { + jsonWriteClass = NSClassFromString(@"SBJSON"); + } + + if (error) *error = nil; + + GTLSBJSON *writer = [[[jsonWriteClass alloc] init] autorelease]; + [writer setHumanReadable:humanReadable]; + NSString *jsonStr = [writer stringWithObject:obj + error:error]; + NSData *data = [jsonStr dataUsingEncoding:NSUTF8StringEncoding]; + return data; + } +#endif +} + ++ (id)objectWithString:(NSString *)jsonStr + error:(NSError **)error { + NSData *data = [jsonStr dataUsingEncoding:NSUTF8StringEncoding]; + return [self objectWithData:data + error:error]; +} + ++ (id)objectWithData:(NSData *)jsonData + error:(NSError **)error { +#if GTL_REQUIRES_NSJSONSERIALIZATION + NSMutableDictionary *obj = [NSJSONSerialization JSONObjectWithData:jsonData + options:NSJSONReadingMutableContainers + error:error]; + return obj; +#else + Class serializer = NSClassFromString(@"NSJSONSerialization"); + if (serializer) { + const NSUInteger kOpts = (1UL << 0); // NSJSONReadingMutableContainers + NSMutableDictionary *obj = [serializer JSONObjectWithData:jsonData + options:kOpts + error:error]; + return obj; + } else { + Class jsonParseClass = NSClassFromString(@"SBJsonParser"); + if (!jsonParseClass) { + jsonParseClass = NSClassFromString(@"SBJSON"); + } + + if (error) *error = nil; + + GTLSBJSON *parser = [[[jsonParseClass alloc] init] autorelease]; + + NSString *jsonrep = [[[NSString alloc] initWithData:jsonData + encoding:NSUTF8StringEncoding] autorelease]; + id obj = [parser objectWithString:jsonrep + error:error]; + return obj; + } +#endif +} + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLObject.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLObject.h new file mode 100644 index 0000000000..43935adfaa --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLObject.h @@ -0,0 +1,208 @@ +/* Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLObject.h +// + +// GTLObject documentation: +// https://code.google.com/p/google-api-objectivec-client/wiki/Introduction#Objects_and_Queries + +#import + +#import "GTLDefines.h" +#import "GTLUtilities.h" +#import "GTLDateTime.h" + +#undef _EXTERN +#undef _INITIALIZE_AS +#ifdef GTLOBJECT_DEFINE_GLOBALS +#define _EXTERN +#define _INITIALIZE_AS(x) =x +#else +#define _EXTERN extern +#define _INITIALIZE_AS(x) +#endif + +@protocol GTLCollectionProtocol +@optional +@property (retain) NSArray *items; +@end + +@protocol GTLBatchItemCreationProtocol +- (void)createItemsWithClassMap:(NSDictionary *)batchClassMap; +@end + +@interface GTLObject : NSObject { + + @private + + NSMutableDictionary *json_; + + // Used when creating the subobjects from this one. + NSDictionary *surrogates_; + + // Any complex object hung off this object goes into the cache so the + // next fetch will get the same object back instead of having to recreate + // it. + NSMutableDictionary *childCache_; + + // Anything defined by the client; retained but not used internally; not + // copied by copyWithZone: + NSMutableDictionary *userProperties_; +} + +@property (nonatomic, retain) NSMutableDictionary *JSON; +@property (nonatomic, retain) NSDictionary *surrogates; +@property (nonatomic, retain) NSMutableDictionary *userProperties; + +/////////////////////////////////////////////////////////////////////////////// +// +// Public methods +// +// These methods are intended for users of the library +// + ++ (id)object; ++ (id)objectWithJSON:(NSMutableDictionary *)dict; + +- (id)copyWithZone:(NSZone *)zone; + +- (NSString *)JSONString; + +// generic access to json; also creates it if necessary +- (void)setJSONValue:(id)obj forKey:(NSString *)key GTL_NONNULL((2)); +- (id)JSONValueForKey:(NSString *)key; + +// Returns the list of keys in this object's JSON that aren't listed as +// properties on the object. +- (NSArray *)additionalJSONKeys; + +// Any keys in the JSON that aren't listed as @properties on the object +// are counted as "additional properties". These allow you to get/set them. +- (id)additionalPropertyForName:(NSString *)name; +- (void)setAdditionalProperty:(id)obj forName:(NSString *)name GTL_NONNULL((2)); +- (NSDictionary *)additionalProperties; + +// User properties are supported for client convenience, but are not copied by +// copyWithZone. User Properties keys beginning with _ are reserved by the library. +// +// Set nil for obj to remove the property. +- (void)setProperty:(id)obj forKey:(NSString *)key GTL_NONNULL((2)); +- (id)propertyForKey:(NSString *)key GTL_NONNULL((1)); + +// userData is stored as a property with key "_userData" +- (void)setUserData:(id)obj; +- (id)userData; + +// Makes a partial query-compatible string describing the fields present +// in this object. (Note: only the first element of any array is examined.) +// +// http://code.google.com/apis/tasks/v1/performance.html#partial +// +- (NSString *)fieldsDescription; + +// Makes an object containing only the changes needed to do a partial update +// (patch), where the patch would be to change an object from the original +// to the receiver, such as +// +// GTLSomeObject *patchObject = [newVersion patchObjectFromOriginal:oldVersion]; +// +// http://code.google.com/apis/tasks/v1/performance.html#patch +// +// NOTE: this method returns nil if there are no changes between the original +// and the receiver. +- (id)patchObjectFromOriginal:(GTLObject *)original; + +// Method creating a null value to set object properties for patch queries that +// delete fields. Do not use this except when setting an object property for +// a patch query. ++ (id)nullValue; + +/////////////////////////////////////////////////////////////////////////////// +// +// Protected methods +// +// These methods are intended for subclasses of GTLObject +// + +// class registration ("kind" strings) for subclasses ++ (Class)registeredObjectClassForKind:(NSString *)kind; ++ (void)registerObjectClassForKind:(NSString *)kind; + +// creation of objects from a JSON dictionary ++ (GTLObject *)objectForJSON:(NSMutableDictionary *)json + defaultClass:(Class)defaultClass + surrogates:(NSDictionary *)surrogates + batchClassMap:(NSDictionary *)batchClassMap; + +// property-to-key mapping (for JSON keys which are not used as method names) ++ (NSDictionary *)propertyToJSONKeyMap; + +// property-to-Class mapping for array properties (to say what is in the array) ++ (NSDictionary *)arrayPropertyToClassMap; + +// The default class for additional JSON keys ++ (Class)classForAdditionalProperties; + +@end + +// Collection objects with an "items" property should derive from GTLCollection +// object. This provides support for fast object enumeration, the +// itemAtIndex: convenience method, and indexed subscripts. +// +// Subclasses must implement the items method dynamically. +@interface GTLCollectionObject : GTLObject { + @private + NSDictionary *identifierMap_; +} + +// itemAtIndex: and objectAtIndexedSubscript: return nil when the index exceeds +// the bounds of the items array. +- (id)itemAtIndex:(NSUInteger)idx; + +- (id)objectAtIndexedSubscript:(NSInteger)idx; + +// itemForIdentifier: looks up items from the collection object by identifier, +// and returns the first one. +// +// Typically, items will have a unique identifier (with key "id" in the +// object's JSON). This method returns the first item found in the collection +// with the specified identifier. +// +// The first time this method is used, the collection will cache a map of +// identifiers to items. If the items list for the instance somehow changes, +// use the reset method below to force a new cache to be created for this +// collection. +- (id)itemForIdentifier:(NSString *)key GTL_NONNULL((1)); + +// Identifiers for all items are cached when the first one is obtained. +// This method resets the cache. It is needed only if the item list has +// changed. +- (void)resetIdentifierMap; + +@end + +@interface GTLCollectionObject (DynamicMethods) +- (NSArray *)items; +@end + +// Base object use for when an service method directly returns an array instead +// of an object. Normally methods should return an object with an 'items' +// property, but this exists for the methods not up to spec. +@interface GTLResultArray : GTLCollectionObject +// This method should only be called by subclasses. +- (NSArray *)itemsWithItemClass:(Class)itemClass; +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLObject.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLObject.m new file mode 100644 index 0000000000..83c2d19f6c --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLObject.m @@ -0,0 +1,722 @@ +/* Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLObject.m +// + +#define GTLOBJECT_DEFINE_GLOBALS 1 + +#include + +#import "GTLObject.h" +#import "GTLRuntimeCommon.h" +#import "GTLJSONParser.h" + +static NSString *const kUserDataPropertyKey = @"_userData"; + +@interface GTLObject () ++ (NSMutableArray *)allDeclaredProperties; ++ (NSArray *)allKnownKeys; + ++ (NSArray *)fieldsElementsForJSON:(NSDictionary *)targetJSON; ++ (NSString *)fieldsDescriptionForJSON:(NSDictionary *)targetJSON; + ++ (NSMutableDictionary *)patchDictionaryForJSON:(NSDictionary *)newJSON + fromOriginalJSON:(NSDictionary *)originalJSON; +@end + +@implementation GTLObject + +@synthesize JSON = json_, + surrogates = surrogates_, + userProperties = userProperties_; + ++ (id)object { + return [[[self alloc] init] autorelease]; +} + ++ (id)objectWithJSON:(NSMutableDictionary *)dict { + GTLObject *obj = [self object]; + obj.JSON = dict; + return obj; +} + ++ (NSDictionary *)propertyToJSONKeyMap { + return nil; +} + ++ (NSDictionary *)arrayPropertyToClassMap { + return nil; +} + ++ (Class)classForAdditionalProperties { + return Nil; +} + +- (BOOL)isEqual:(GTLObject *)other { + if (self == other) return YES; + if (other == nil) return NO; + + // The objects should be the same class, or one should be a subclass of the + // other's class + if (![other isKindOfClass:[self class]] + && ![self isKindOfClass:[other class]]) return NO; + + // What we're not comparing here: + // properties + return GTL_AreEqualOrBothNil(json_, [other JSON]); +} + +// By definition, for two objects to potentially be considered equal, +// they must have the same hash value. The hash is mostly ignored, +// but removeObjectsInArray: in Leopard does seem to check the hash, +// and NSObject's default hash method just returns the instance pointer. +// We'll define hash here for all of our GTLObjects. +- (NSUInteger)hash { + return (NSUInteger) (void *) [GTLObject class]; +} + +- (id)copyWithZone:(NSZone *)zone { + GTLObject* newObject = [[[self class] allocWithZone:zone] init]; + CFPropertyListRef ref = CFPropertyListCreateDeepCopy(kCFAllocatorDefault, + json_, kCFPropertyListMutableContainers); + GTL_DEBUG_ASSERT(ref != NULL, @"GTLObject: copy failed (probably a non-plist type in the JSON)"); + newObject.JSON = [NSMakeCollectable(ref) autorelease]; + newObject.surrogates = self.surrogates; + + // What we're not copying: + // userProperties + return newObject; +} + +- (NSString *)descriptionWithLocale:(id)locale { + return [self description]; +} + +- (void)dealloc { + [json_ release]; + [surrogates_ release]; + [childCache_ release]; + [userProperties_ release]; + + [super dealloc]; +} + +#pragma mark JSON values + +- (void)setJSONValue:(id)obj forKey:(NSString *)key { + NSMutableDictionary *dict = self.JSON; + if (dict == nil && obj != nil) { + dict = [NSMutableDictionary dictionaryWithCapacity:1]; + self.JSON = dict; + } + [dict setValue:obj forKey:key]; +} + +- (id)JSONValueForKey:(NSString *)key { + id obj = [self.JSON objectForKey:key]; + return obj; +} + +- (NSString *)JSONString { + NSError *error = nil; + NSString *str = [GTLJSONParser stringWithObject:[self JSON] + humanReadable:YES + error:&error]; + if (error) { + return [error description]; + } + return str; +} + +- (NSArray *)additionalJSONKeys { + NSArray *knownKeys = [[self class] allKnownKeys]; + NSMutableArray *result = [NSMutableArray arrayWithArray:[json_ allKeys]]; + [result removeObjectsInArray:knownKeys]; + // Return nil instead of an empty array. + if ([result count] == 0) { + result = nil; + } + return result; +} + +#pragma mark Partial - Fields + +- (NSString *)fieldsDescription { + NSString *str = [GTLObject fieldsDescriptionForJSON:self.JSON]; + return str; +} + ++ (NSString *)fieldsDescriptionForJSON:(NSDictionary *)targetJSON { + // Internal routine: recursively generate a string field description + // by joining elements + NSArray *array = [self fieldsElementsForJSON:targetJSON]; + NSString *str = [array componentsJoinedByString:@","]; + return str; +} + ++ (NSArray *)fieldsElementsForJSON:(NSDictionary *)targetJSON { + // Internal routine: recursively generate an array of field description + // element strings + NSMutableArray *resultFields = [NSMutableArray array]; + + // Sorting the dictionary keys gives us deterministic results when iterating + NSArray *sortedKeys = [[targetJSON allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]; + for (NSString *key in sortedKeys) { + // We'll build a comma-separated list of fields + id value = [targetJSON objectForKey:key]; + if ([value isKindOfClass:[NSString class]] + || [value isKindOfClass:[NSNumber class]]) { + // Basic type (string, number), so the key is what we want + [resultFields addObject:key]; + } else if ([value isKindOfClass:[NSDictionary class]]) { + // Object (dictionary): "parent/child1,parent/child2,parent/child3" + NSArray *subElements = [self fieldsElementsForJSON:value]; + for (NSString *subElem in subElements) { + NSString *prepended = [NSString stringWithFormat:@"%@/%@", + key, subElem]; + [resultFields addObject:prepended]; + } + } else if ([value isKindOfClass:[NSArray class]]) { + // Array; we'll generate from the first array entry: + // "parent(child1,child2,child3)" + // + // Open question: should this instead create the union of elements for + // all items in the array, rather than just get fields from the first + // array object? + if ([(NSArray *)value count] > 0) { + id firstObj = [value objectAtIndex:0]; + if ([firstObj isKindOfClass:[NSDictionary class]]) { + // An array of objects + NSString *contentsStr = [self fieldsDescriptionForJSON:firstObj]; + NSString *encapsulated = [NSString stringWithFormat:@"%@(%@)", + key, contentsStr]; + [resultFields addObject:encapsulated]; + } else { + // An array of some basic type, or of arrays + [resultFields addObject:key]; + } + } + } else { + GTL_ASSERT(0, @"GTLObject unknown field element for %@ (%@)", + key, NSStringFromClass([value class])); + } + } + return resultFields; +} + +#pragma mark Partial - Patch + +- (id)patchObjectFromOriginal:(GTLObject *)original { + id resultObj; + NSMutableDictionary *resultJSON = [GTLObject patchDictionaryForJSON:self.JSON + fromOriginalJSON:original.JSON]; + if ([resultJSON count] > 0) { + resultObj = [[self class] objectWithJSON:resultJSON]; + } else { + // Client apps should not attempt to patch with an object containing + // empty JSON + resultObj = nil; + } + return resultObj; +} + ++ (NSMutableDictionary *)patchDictionaryForJSON:(NSDictionary *)newJSON + fromOriginalJSON:(NSDictionary *)originalJSON { + // Internal recursive routine to create an object suitable for + // our patch semantics + NSMutableDictionary *resultJSON = [NSMutableDictionary dictionary]; + + // Iterate through keys present in the old object + NSArray *originalKeys = [originalJSON allKeys]; + for (NSString *key in originalKeys) { + id originalValue = [originalJSON objectForKey:key]; + id newValue = [newJSON valueForKey:key]; + if (newValue == nil) { + // There is no new value for this key, so set the value to NSNull + [resultJSON setValue:[NSNull null] forKey:key]; + } else if (!GTL_AreEqualOrBothNil(originalValue, newValue)) { + // The values for this key differ + if ([originalValue isKindOfClass:[NSDictionary class]] + && [newValue isKindOfClass:[NSDictionary class]]) { + // Both are objects; recurse + NSMutableDictionary *subDict = [self patchDictionaryForJSON:newValue + fromOriginalJSON:originalValue]; + [resultJSON setValue:subDict forKey:key]; + } else { + // They are non-object values; the new replaces the old. Per the + // documentation for patch, this replaces entire arrays. + [resultJSON setValue:newValue forKey:key]; + } + } else { + // The values are the same; omit this key-value pair + } + } + + // Iterate through keys present only in the new object, and add them to the + // result + NSMutableArray *newKeys = [NSMutableArray arrayWithArray:[newJSON allKeys]]; + [newKeys removeObjectsInArray:originalKeys]; + + for (NSString *key in newKeys) { + id value = [newJSON objectForKey:key]; + [resultJSON setValue:value forKey:key]; + } + return resultJSON; +} + ++ (id)nullValue { + return [NSNull null]; +} + +#pragma mark Additional Properties + +- (id)additionalPropertyForName:(NSString *)name { + // Return the cached object, if any, before creating one. + id result = [self cacheChildForKey:name]; + if (result != nil) { + return result; + } + + Class defaultClass = [[self class] classForAdditionalProperties]; + id jsonObj = [self JSONValueForKey:name]; + BOOL shouldCache = NO; + if (jsonObj != nil) { + NSDictionary *surrogates = self.surrogates; + result = [GTLRuntimeCommon objectFromJSON:jsonObj + defaultClass:defaultClass + surrogates:surrogates + isCacheable:&shouldCache]; + } + + [self setCacheChild:(shouldCache ? result : nil) + forKey:name]; + return result; +} + +- (void)setAdditionalProperty:(id)obj forName:(NSString *)name { + BOOL shouldCache = NO; + Class defaultClass = [[self class] classForAdditionalProperties]; + id json = [GTLRuntimeCommon jsonFromAPIObject:obj + expectedClass:defaultClass + isCacheable:&shouldCache]; + [self setJSONValue:json forKey:name]; + [self setCacheChild:(shouldCache ? obj : nil) + forKey:name]; +} + +- (NSDictionary *)additionalProperties { + NSMutableDictionary *result = [NSMutableDictionary dictionary]; + + NSArray *propertyNames = [self additionalJSONKeys]; + for (NSString *name in propertyNames) { + id obj = [self additionalPropertyForName:name]; + [result setObject:obj forKey:name]; + } + + return result; +} + +#pragma mark Child Cache methods + +// There is no property for childCache_ as there shouldn't be KVC/KVO +// support for it, it's an implementation detail. + +- (void)setCacheChild:(id)obj forKey:(NSString *)key { + if (childCache_ == nil && obj != nil) { + childCache_ = [[NSMutableDictionary alloc] initWithObjectsAndKeys: + obj, key, nil]; + } else { + [childCache_ setValue:obj forKey:key]; + } +} + +- (id)cacheChildForKey:(NSString *)key { + id obj = [childCache_ objectForKey:key]; + return obj; +} + +#pragma mark userData and user properties + +- (void)setUserData:(id)userData { + [self setProperty:userData forKey:kUserDataPropertyKey]; +} + +- (id)userData { + // be sure the returned pointer has the life of the autorelease pool, + // in case self is released immediately + return [[[self propertyForKey:kUserDataPropertyKey] retain] autorelease]; +} + +- (void)setProperty:(id)obj forKey:(NSString *)key { + if (obj == nil) { + // user passed in nil, so delete the property + [userProperties_ removeObjectForKey:key]; + } else { + // be sure the property dictionary exists + if (userProperties_ == nil) { + self.userProperties = [NSMutableDictionary dictionary]; + } + [userProperties_ setObject:obj forKey:key]; + } +} + +- (id)propertyForKey:(NSString *)key { + id obj = [userProperties_ objectForKey:key]; + + // be sure the returned pointer has the life of the autorelease pool, + // in case self is released immediately + return [[obj retain] autorelease]; +} + +#pragma mark Support methods + ++ (NSMutableArray *)allDeclaredProperties { + NSMutableArray *array = [NSMutableArray array]; + + // walk from this class up the hierarchy to GTLObject + Class topClass = class_getSuperclass([GTLObject class]); + for (Class currClass = self; + currClass != topClass; + currClass = class_getSuperclass(currClass)) { + // step through this class's properties, and add the property names to the + // array + objc_property_t *properties = class_copyPropertyList(currClass, NULL); + if (properties) { + for (objc_property_t *prop = properties; + *prop != NULL; + ++prop) { + const char *propName = property_getName(*prop); + // We only want dynamic properties; their attributes contain ",D". + const char *attr = property_getAttributes(*prop); + const char *dynamicMarker = strstr(attr, ",D"); + if (dynamicMarker && + (dynamicMarker[2] == 0 || dynamicMarker[2] == ',' )) { + [array addObject:[NSString stringWithUTF8String:propName]]; + } + } + free(properties); + } + } + return array; +} + ++ (NSArray *)allKnownKeys { + NSArray *allProps = [self allDeclaredProperties]; + NSMutableArray *knownKeys = [NSMutableArray arrayWithArray:allProps]; + + NSDictionary *propMap = [GTLObject propertyToJSONKeyMapForClass:[self class]]; + + NSUInteger idx = 0; + for (NSString *propName in allProps) { + NSString *jsonKey = [propMap objectForKey:propName]; + if (jsonKey) { + [knownKeys replaceObjectAtIndex:idx + withObject:jsonKey]; + } + ++idx; + } + return knownKeys; +} + +- (NSString *)description { + // find the list of declared and otherwise known JSON keys for this class + NSArray *knownKeys = [[self class] allKnownKeys]; + + NSMutableString *descStr = [NSMutableString string]; + + NSString *spacer = @""; + for (NSString *key in json_) { + NSString *value = nil; + // show question mark for JSON keys not supported by a declared property: + // foo?:"Hi mom." + NSString *qmark = [knownKeys containsObject:key] ? @"" : @"?"; + + // determine property value to dislay + id rawValue = [json_ valueForKey:key]; + if ([rawValue isKindOfClass:[NSDictionary class]]) { + // for dictionaries, show the list of keys: + // {key1,key2,key3} + NSString *subkeyList = [[rawValue allKeys] componentsJoinedByString:@","]; + value = [NSString stringWithFormat:@"{%@}", subkeyList]; + } else if ([rawValue isKindOfClass:[NSArray class]]) { + // for arrays, show the number of items in the array: + // [3] + value = [NSString stringWithFormat:@"[%lu]", (unsigned long)[(NSArray *)rawValue count]]; + } else if ([rawValue isKindOfClass:[NSString class]]) { + // for strings, show the string in quotes: + // "Hi mom." + value = [NSString stringWithFormat:@"\"%@\"", rawValue]; + } else { + // for numbers, show just the number + value = [rawValue description]; + } + [descStr appendFormat:@"%@%@%@:%@", spacer, key, qmark, value]; + spacer = @" "; + } + + NSString *str = [NSString stringWithFormat:@"%@ %p: {%@}", + [self class], self, descStr]; + return str; +} + +#pragma mark Class Registration + +static NSMutableDictionary *gKindMap = nil; + ++ (Class)registeredObjectClassForKind:(NSString *)kind { + Class resultClass = [gKindMap objectForKey:kind]; + return resultClass; +} + ++ (void)registerObjectClassForKind:(NSString *)kind { + // there's no autorelease pool in place at +load time, so we'll create our own + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + if (gKindMap == nil) { + gKindMap = [GTLUtilities newStaticDictionary]; + } + + Class selfClass = [self class]; + +#if DEBUG + // ensure this is a unique registration + if ([gKindMap objectForKey:kind] != nil ) { + GTL_DEBUG_LOG(@"%@ (%@) registration conflicts with %@", + selfClass, kind, [gKindMap objectForKey:kind]); + } + if ([[gKindMap allKeysForObject:selfClass] count] != 0) { + GTL_DEBUG_LOG(@"%@ (%@) registration conflicts with %@", + selfClass, kind, [gKindMap allKeysForObject:selfClass]); + } +#endif + + [gKindMap setValue:selfClass forKey:kind]; + + // we drain here to keep the clang static analyzer quiet + [pool drain]; +} + +#pragma mark Object Instantiation + ++ (GTLObject *)objectForJSON:(NSMutableDictionary *)json + defaultClass:(Class)defaultClass + surrogates:(NSDictionary *)surrogates + batchClassMap:(NSDictionary *)batchClassMap { + if ([json count] == 0 || [json isEqual:[NSNull null]]) { + // no actual result, such as the response from a delete + return nil; + } + + // Determine the class to instantiate, based on the original fetch + // request or by looking up "kind" string from the registration at + // +load time of GTLObject subclasses + // + // We're letting the dynamic kind override the default class so + // feeds of heterogenous entries can use the defaultClass as a + // fallback + Class classToCreate = defaultClass; + NSString *kind = nil; + if ([json isKindOfClass:[NSDictionary class]]) { + kind = [json valueForKey:@"kind"]; + if ([kind isKindOfClass:[NSString class]] && [kind length] > 0) { + Class dynamicClass = [GTLObject registeredObjectClassForKind:kind]; + if (dynamicClass) { + classToCreate = dynamicClass; + } + } + } + + // Warn the developer that no specific class of GTLObject + // was requested with the fetch call, and no class is found + // compiled in to match the "kind" attribute of the JSON + // returned by the server + GTL_ASSERT(classToCreate != nil, + @"Could not find registered GTLObject subclass to " + "match JSON with kind \"%@\"", kind); + + if (classToCreate == nil) { + classToCreate = [self class]; + } + + // See if the top-level class for the JSON is listed in the surrogates; + // if so, instantiate the surrogate class instead + Class baseSurrogate = [surrogates objectForKey:classToCreate]; + if (baseSurrogate) { + classToCreate = baseSurrogate; + } + + // now instantiate the GTLObject + GTLObject *parsedObject = [classToCreate object]; + + parsedObject.surrogates = surrogates; + parsedObject.JSON = json; + + // it's time to instantiate inner items + if ([parsedObject conformsToProtocol:@protocol(GTLBatchItemCreationProtocol)]) { + id batch = + (id ) parsedObject; + [batch createItemsWithClassMap:batchClassMap]; + } + + return parsedObject; +} + +#pragma mark Runtime Utilities + +static NSMutableDictionary *gJSONKeyMapCache = nil; +static NSMutableDictionary *gArrayPropertyToClassMapCache = nil; + ++ (void)initialize { + // Note that initialize is guaranteed by the runtime to be called in a + // thread-safe manner + if (gJSONKeyMapCache == nil) { + gJSONKeyMapCache = [GTLUtilities newStaticDictionary]; + } + if (gArrayPropertyToClassMapCache == nil) { + gArrayPropertyToClassMapCache = [GTLUtilities newStaticDictionary]; + } +} + ++ (NSDictionary *)propertyToJSONKeyMapForClass:(Class)aClass { + NSDictionary *resultMap = + [GTLUtilities mergedClassDictionaryForSelector:@selector(propertyToJSONKeyMap) + startClass:aClass + ancestorClass:[GTLObject class] + cache:gJSONKeyMapCache]; + return resultMap; +} + ++ (NSDictionary *)arrayPropertyToClassMapForClass:(Class)aClass { + NSDictionary *resultMap = + [GTLUtilities mergedClassDictionaryForSelector:@selector(arrayPropertyToClassMap) + startClass:aClass + ancestorClass:[GTLObject class] + cache:gArrayPropertyToClassMapCache]; + return resultMap; +} + +#pragma mark Runtime Support + ++ (Class)ancestorClass { + return [GTLObject class]; +} + ++ (BOOL)resolveInstanceMethod:(SEL)sel { + BOOL resolved = [GTLRuntimeCommon resolveInstanceMethod:sel onClass:self]; + if (resolved) + return YES; + + return [super resolveInstanceMethod:sel]; +} + +@end + +@implementation GTLCollectionObject +// Subclasses must implement the items method dynamically. + +- (void)dealloc { + [identifierMap_ release]; + [super dealloc]; +} + +- (id)itemAtIndex:(NSUInteger)idx { + NSArray *items = [self performSelector:@selector(items)]; + if (idx < [items count]) { + return [items objectAtIndex:idx]; + } + return nil; +} + +- (id)objectAtIndexedSubscript:(NSInteger)idx { + if (idx >= 0) { + return [self itemAtIndex:(NSUInteger)idx]; + } + return nil; +} + +- (id)itemForIdentifier:(NSString *)key { + if (identifierMap_ == nil) { + NSArray *items = [self performSelector:@selector(items)]; + NSMutableDictionary *dict = + [NSMutableDictionary dictionaryWithCapacity:[items count]]; + for (id item in items) { + id identifier = [item valueForKey:@"identifier"]; + if (identifier != nil && identifier != [NSNull null]) { + if ([dict objectForKey:identifier] == nil) { + [dict setObject:item forKey:identifier]; + } + } + } + identifierMap_ = [dict copy]; + } + return [identifierMap_ objectForKey:key]; +} + +- (void)resetIdentifierMap { + [identifierMap_ release]; + identifierMap_ = nil; +} + +// NSFastEnumeration protocol +- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state + objects:(id *)stackbuf + count:(NSUInteger)len { + NSArray *items = [self performSelector:@selector(items)]; + NSUInteger result = [items countByEnumeratingWithState:state + objects:stackbuf + count:len]; + return result; +} + +@end + +@implementation GTLResultArray + +- (NSArray *)itemsWithItemClass:(Class)itemClass { + // Return the cached array before creating on demand. + NSString *cacheKey = @"result_array_items"; + NSMutableArray *cachedArray = [self cacheChildForKey:cacheKey]; + if (cachedArray != nil) { + return cachedArray; + } + NSArray *result = nil; + NSArray *array = (NSArray *)[self JSON]; + if (array != nil) { + if ([array isKindOfClass:[NSArray class]]) { + NSDictionary *surrogates = self.surrogates; + result = [GTLRuntimeCommon objectFromJSON:array + defaultClass:itemClass + surrogates:surrogates + isCacheable:NULL]; + } else { +#if DEBUG + if (![array isKindOfClass:[NSNull class]]) { + GTL_DEBUG_LOG(@"GTLObject: unexpected JSON: %@ should be an array, actually is a %@:\n%@", + NSStringFromClass([self class]), + NSStringFromClass([array class]), + array); + } +#endif + result = array; + } + } + + [self setCacheChild:result forKey:cacheKey]; + return result; +} + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlus.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlus.h new file mode 100644 index 0000000000..220410bb2b --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlus.h @@ -0,0 +1,44 @@ +/* Copyright (c) 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLPlus.h +// + +// ---------------------------------------------------------------------------- +// NOTE: This file is generated from Google APIs Discovery Service. +// Service: +// Google+ API (plus/v1) +// Description: +// The Google+ API enables developers to build on top of the Google+ platform. +// Documentation: +// https://developers.google.com/+/api/ + +#import "GTLPlusConstants.h" + +#import "GTLPlusAcl.h" +#import "GTLPlusAclentryResource.h" +#import "GTLPlusActivity.h" +#import "GTLPlusActivityFeed.h" +#import "GTLPlusComment.h" +#import "GTLPlusCommentFeed.h" +#import "GTLPlusItemScope.h" +#import "GTLPlusMoment.h" +#import "GTLPlusMomentsFeed.h" +#import "GTLPlusPeopleFeed.h" +#import "GTLPlusPerson.h" + +#import "GTLQueryPlus.h" +#import "GTLServicePlus.h" diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusAcl.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusAcl.h new file mode 100644 index 0000000000..aad4f65ef7 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusAcl.h @@ -0,0 +1,60 @@ +/* Copyright (c) 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLPlusAcl.h +// + +// ---------------------------------------------------------------------------- +// NOTE: This file is generated from Google APIs Discovery Service. +// Service: +// Google+ API (plus/v1) +// Description: +// The Google+ API enables developers to build on top of the Google+ platform. +// Documentation: +// https://developers.google.com/+/api/ +// Classes: +// GTLPlusAcl (0 custom class methods, 3 custom properties) + +#if GTL_BUILT_AS_FRAMEWORK + #import "GTL/GTLObject.h" +#else + #import "GTLObject.h" +#endif + +@class GTLPlusAclentryResource; + +// ---------------------------------------------------------------------------- +// +// GTLPlusAcl +// + +// This class supports NSFastEnumeration over its "items" property. It also +// supports -itemAtIndex: to retrieve individual objects from "items". + +@interface GTLPlusAcl : GTLCollectionObject + +// Description of the access granted, suitable for display. +// Remapped to 'descriptionProperty' to avoid NSObject's 'description'. +@property (copy) NSString *descriptionProperty; + +// The list of access entries. +@property (retain) NSArray *items; // of GTLPlusAclentryResource + +// Identifies this resource as a collection of access controls. Value: +// "plus#acl". +@property (copy) NSString *kind; + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusAcl.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusAcl.m new file mode 100644 index 0000000000..0e82d087b4 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusAcl.m @@ -0,0 +1,61 @@ +/* Copyright (c) 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLPlusAcl.m +// + +// ---------------------------------------------------------------------------- +// NOTE: This file is generated from Google APIs Discovery Service. +// Service: +// Google+ API (plus/v1) +// Description: +// The Google+ API enables developers to build on top of the Google+ platform. +// Documentation: +// https://developers.google.com/+/api/ +// Classes: +// GTLPlusAcl (0 custom class methods, 3 custom properties) + +#import "GTLPlusAcl.h" + +#import "GTLPlusAclentryResource.h" + +// ---------------------------------------------------------------------------- +// +// GTLPlusAcl +// + +@implementation GTLPlusAcl +@dynamic descriptionProperty, items, kind; + ++ (NSDictionary *)propertyToJSONKeyMap { + NSDictionary *map = + [NSDictionary dictionaryWithObject:@"description" + forKey:@"descriptionProperty"]; + return map; +} + ++ (NSDictionary *)arrayPropertyToClassMap { + NSDictionary *map = + [NSDictionary dictionaryWithObject:[GTLPlusAclentryResource class] + forKey:@"items"]; + return map; +} + ++ (void)load { + [self registerObjectClassForKind:@"plus#acl"]; +} + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusAclentryResource.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusAclentryResource.h new file mode 100644 index 0000000000..30634e8d17 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusAclentryResource.h @@ -0,0 +1,61 @@ +/* Copyright (c) 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLPlusAclentryResource.h +// + +// ---------------------------------------------------------------------------- +// NOTE: This file is generated from Google APIs Discovery Service. +// Service: +// Google+ API (plus/v1) +// Description: +// The Google+ API enables developers to build on top of the Google+ platform. +// Documentation: +// https://developers.google.com/+/api/ +// Classes: +// GTLPlusAclentryResource (0 custom class methods, 3 custom properties) + +#if GTL_BUILT_AS_FRAMEWORK + #import "GTL/GTLObject.h" +#else + #import "GTLObject.h" +#endif + +// ---------------------------------------------------------------------------- +// +// GTLPlusAclentryResource +// + +@interface GTLPlusAclentryResource : GTLObject + +// A descriptive name for this entry. Suitable for display. +@property (copy) NSString *displayName; + +// The ID of the entry. For entries of type "person" or "circle", this is the ID +// of the resource. For other types, this property is not set. +// identifier property maps to 'id' in JSON (to avoid Objective C's 'id'). +@property (copy) NSString *identifier; + +// The type of entry describing to whom access is granted. Possible values are: +// - "person" - Access to an individual. +// - "circle" - Access to members of a circle. +// - "myCircles" - Access to members of all the person's circles. +// - "extendedCircles" - Access to members of everyone in a person's circles, +// plus all of the people in their circles. +// - "public" - Access to anyone on the web. +@property (copy) NSString *type; + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusAclentryResource.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusAclentryResource.m new file mode 100644 index 0000000000..ff6402905f --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusAclentryResource.m @@ -0,0 +1,48 @@ +/* Copyright (c) 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLPlusAclentryResource.m +// + +// ---------------------------------------------------------------------------- +// NOTE: This file is generated from Google APIs Discovery Service. +// Service: +// Google+ API (plus/v1) +// Description: +// The Google+ API enables developers to build on top of the Google+ platform. +// Documentation: +// https://developers.google.com/+/api/ +// Classes: +// GTLPlusAclentryResource (0 custom class methods, 3 custom properties) + +#import "GTLPlusAclentryResource.h" + +// ---------------------------------------------------------------------------- +// +// GTLPlusAclentryResource +// + +@implementation GTLPlusAclentryResource +@dynamic displayName, identifier, type; + ++ (NSDictionary *)propertyToJSONKeyMap { + NSDictionary *map = + [NSDictionary dictionaryWithObject:@"id" + forKey:@"identifier"]; + return map; +} + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusActivity.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusActivity.h new file mode 100644 index 0000000000..ce4b941741 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusActivity.h @@ -0,0 +1,493 @@ +/* Copyright (c) 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLPlusActivity.h +// + +// ---------------------------------------------------------------------------- +// NOTE: This file is generated from Google APIs Discovery Service. +// Service: +// Google+ API (plus/v1) +// Description: +// The Google+ API enables developers to build on top of the Google+ platform. +// Documentation: +// https://developers.google.com/+/api/ +// Classes: +// GTLPlusActivity (0 custom class methods, 19 custom properties) +// GTLPlusActivityActor (0 custom class methods, 5 custom properties) +// GTLPlusActivityObject (0 custom class methods, 10 custom properties) +// GTLPlusActivityProvider (0 custom class methods, 1 custom properties) +// GTLPlusActivityActorImage (0 custom class methods, 1 custom properties) +// GTLPlusActivityActorName (0 custom class methods, 2 custom properties) +// GTLPlusActivityObjectActor (0 custom class methods, 4 custom properties) +// GTLPlusActivityObjectAttachmentsItem (0 custom class methods, 9 custom properties) +// GTLPlusActivityObjectPlusoners (0 custom class methods, 2 custom properties) +// GTLPlusActivityObjectReplies (0 custom class methods, 2 custom properties) +// GTLPlusActivityObjectResharers (0 custom class methods, 2 custom properties) +// GTLPlusActivityObjectActorImage (0 custom class methods, 1 custom properties) +// GTLPlusActivityObjectAttachmentsItemEmbed (0 custom class methods, 2 custom properties) +// GTLPlusActivityObjectAttachmentsItemFullImage (0 custom class methods, 4 custom properties) +// GTLPlusActivityObjectAttachmentsItemImage (0 custom class methods, 4 custom properties) +// GTLPlusActivityObjectAttachmentsItemThumbnailsItem (0 custom class methods, 3 custom properties) +// GTLPlusActivityObjectAttachmentsItemThumbnailsItemImage (0 custom class methods, 4 custom properties) + +#if GTL_BUILT_AS_FRAMEWORK + #import "GTL/GTLObject.h" +#else + #import "GTLObject.h" +#endif + +@class GTLPlusAcl; +@class GTLPlusActivityActor; +@class GTLPlusActivityActorImage; +@class GTLPlusActivityActorName; +@class GTLPlusActivityObject; +@class GTLPlusActivityObjectActor; +@class GTLPlusActivityObjectActorImage; +@class GTLPlusActivityObjectAttachmentsItem; +@class GTLPlusActivityObjectAttachmentsItemEmbed; +@class GTLPlusActivityObjectAttachmentsItemFullImage; +@class GTLPlusActivityObjectAttachmentsItemImage; +@class GTLPlusActivityObjectAttachmentsItemThumbnailsItem; +@class GTLPlusActivityObjectAttachmentsItemThumbnailsItemImage; +@class GTLPlusActivityObjectPlusoners; +@class GTLPlusActivityObjectReplies; +@class GTLPlusActivityObjectResharers; +@class GTLPlusActivityProvider; + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivity +// + +@interface GTLPlusActivity : GTLObject + +// Identifies who has access to see this activity. +@property (retain) GTLPlusAcl *access; + +// The person who performed this activity. +@property (retain) GTLPlusActivityActor *actor; + +// Street address where this activity occurred. +@property (copy) NSString *address; + +// Additional content added by the person who shared this activity, applicable +// only when resharing an activity. +@property (copy) NSString *annotation; + +// If this activity is a crosspost from another system, this property specifies +// the ID of the original activity. +@property (copy) NSString *crosspostSource; + +// ETag of this response for caching purposes. +@property (copy) NSString *ETag; + +// Latitude and longitude where this activity occurred. Format is latitude +// followed by longitude, space separated. +@property (copy) NSString *geocode; + +// The ID of this activity. +// identifier property maps to 'id' in JSON (to avoid Objective C's 'id'). +@property (copy) NSString *identifier; + +// Identifies this resource as an activity. Value: "plus#activity". +@property (copy) NSString *kind; + +// The object of this activity. +@property (retain) GTLPlusActivityObject *object; + +// ID of the place where this activity occurred. +@property (copy) NSString *placeId; + +// Name of the place where this activity occurred. +@property (copy) NSString *placeName; + +// The service provider that initially published this activity. +@property (retain) GTLPlusActivityProvider *provider; + +// The time at which this activity was initially published. Formatted as an RFC +// 3339 timestamp. +@property (retain) GTLDateTime *published; + +// Radius, in meters, of the region where this activity occurred, centered at +// the latitude and longitude identified in geocode. +@property (copy) NSString *radius; + +// Title of this activity. +@property (copy) NSString *title; + +// The time at which this activity was last updated. Formatted as an RFC 3339 +// timestamp. +@property (retain) GTLDateTime *updated; + +// The link to this activity. +@property (copy) NSString *url; + +// This activity's verb, indicating what action was performed. Possible values +// are: +// - "post" - Publish content to the stream. +// - "share" - Reshare an activity. +@property (copy) NSString *verb; + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityActor +// + +@interface GTLPlusActivityActor : GTLObject + +// The name of the actor, suitable for display. +@property (copy) NSString *displayName; + +// The ID of the actor's person resource. +// identifier property maps to 'id' in JSON (to avoid Objective C's 'id'). +@property (copy) NSString *identifier; + +// The image representation of the actor. +@property (retain) GTLPlusActivityActorImage *image; + +// An object representation of the individual components of name. +@property (retain) GTLPlusActivityActorName *name; + +// The link to the actor's Google profile. +@property (copy) NSString *url; + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityObject +// + +@interface GTLPlusActivityObject : GTLObject + +// If this activity's object is itself another activity (for example, when a +// person reshares an activity), this property specifies the original activity's +// actor. +@property (retain) GTLPlusActivityObjectActor *actor; + +// The media objects attached to this activity. +@property (retain) NSArray *attachments; // of GTLPlusActivityObjectAttachmentsItem + +// The HTML-formatted content, suitable for display. +@property (copy) NSString *content; + +// The ID of the object. When resharing an activity, this is the ID of the +// activity being reshared. +// identifier property maps to 'id' in JSON (to avoid Objective C's 'id'). +@property (copy) NSString *identifier; + +// The type of the object. Possible values are: +// - "note" - Textual content. +// - "activity" - A Google+ activity. +@property (copy) NSString *objectType; + +// The content (text) as provided by the author, stored without any HTML +// formatting. When creating or updating an activity, this value must be +// supplied as plain text in the request. +@property (copy) NSString *originalContent; + +// People who +1'd this activity. +@property (retain) GTLPlusActivityObjectPlusoners *plusoners; + +// Comments in reply to this activity. +@property (retain) GTLPlusActivityObjectReplies *replies; + +// People who reshared this activity. +@property (retain) GTLPlusActivityObjectResharers *resharers; + +// The URL that points to the linked resource. +@property (copy) NSString *url; + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityProvider +// + +@interface GTLPlusActivityProvider : GTLObject + +// Name of the service provider. +@property (copy) NSString *title; + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityActorImage +// + +@interface GTLPlusActivityActorImage : GTLObject + +// The URL of the actor's profile photo. To re-size the image and crop it to a +// square, append the query string ?sz=x, where x is the dimension in pixels of +// each side. +@property (copy) NSString *url; + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityActorName +// + +@interface GTLPlusActivityActorName : GTLObject + +// The family name (last name) of the actor. +@property (copy) NSString *familyName; + +// The given name (first name) of the actor. +@property (copy) NSString *givenName; + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityObjectActor +// + +@interface GTLPlusActivityObjectActor : GTLObject + +// The original actor's name, suitable for display. +@property (copy) NSString *displayName; + +// ID of the original actor. +// identifier property maps to 'id' in JSON (to avoid Objective C's 'id'). +@property (copy) NSString *identifier; + +// The image representation of the original actor. +@property (retain) GTLPlusActivityObjectActorImage *image; + +// A link to the original actor's Google profile. +@property (copy) NSString *url; + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityObjectAttachmentsItem +// + +@interface GTLPlusActivityObjectAttachmentsItem : GTLObject + +// If the attachment is an article, this property contains a snippet of text +// from the article. It can also include descriptions for other types. +@property (copy) NSString *content; + +// The title of the attachment (such as a photo caption or an article title). +@property (copy) NSString *displayName; + +// If the attachment is a video, the embeddable link. +@property (retain) GTLPlusActivityObjectAttachmentsItemEmbed *embed; + +// The full image URL for photo attachments. +@property (retain) GTLPlusActivityObjectAttachmentsItemFullImage *fullImage; + +// The ID of the attachment. +// identifier property maps to 'id' in JSON (to avoid Objective C's 'id'). +@property (copy) NSString *identifier; + +// The preview image for photos or videos. +@property (retain) GTLPlusActivityObjectAttachmentsItemImage *image; + +// The type of media object. Possible values are: +// - "photo" - A photo. +// - "album" - A photo album. +// - "video" - A video. +// - "article" - An article, specified by a link. +@property (copy) NSString *objectType; + +// If the attachment is an album, potential additional thumbnails from the +// album. +@property (retain) NSArray *thumbnails; // of GTLPlusActivityObjectAttachmentsItemThumbnailsItem + +// The link to the attachment, should be of type text/html. +@property (copy) NSString *url; + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityObjectPlusoners +// + +@interface GTLPlusActivityObjectPlusoners : GTLObject + +// The URL for the collection of people who +1'd this activity. +@property (copy) NSString *selfLink; + +// Total number of people who +1'd this activity. +@property (retain) NSNumber *totalItems; // unsignedIntValue + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityObjectReplies +// + +@interface GTLPlusActivityObjectReplies : GTLObject + +// The URL for the collection of comments in reply to this activity. +@property (copy) NSString *selfLink; + +// Total number of comments on this activity. +@property (retain) NSNumber *totalItems; // unsignedIntValue + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityObjectResharers +// + +@interface GTLPlusActivityObjectResharers : GTLObject + +// The URL for the collection of resharers. +@property (copy) NSString *selfLink; + +// Total number of people who reshared this activity. +@property (retain) NSNumber *totalItems; // unsignedIntValue + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityObjectActorImage +// + +@interface GTLPlusActivityObjectActorImage : GTLObject + +// A URL that points to a thumbnail photo of the original actor. +@property (copy) NSString *url; + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityObjectAttachmentsItemEmbed +// + +@interface GTLPlusActivityObjectAttachmentsItemEmbed : GTLObject + +// Media type of the link. +@property (copy) NSString *type; + +// URL of the link. +@property (copy) NSString *url; + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityObjectAttachmentsItemFullImage +// + +@interface GTLPlusActivityObjectAttachmentsItemFullImage : GTLObject + +// The height, in pixels, of the linked resource. +@property (retain) NSNumber *height; // unsignedIntValue + +// Media type of the link. +@property (copy) NSString *type; + +// URL to the image. +@property (copy) NSString *url; + +// The width, in pixels, of the linked resource. +@property (retain) NSNumber *width; // unsignedIntValue + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityObjectAttachmentsItemImage +// + +@interface GTLPlusActivityObjectAttachmentsItemImage : GTLObject + +// The height, in pixels, of the linked resource. +@property (retain) NSNumber *height; // unsignedIntValue + +// Media type of the link. +@property (copy) NSString *type; + +// Image url. +@property (copy) NSString *url; + +// The width, in pixels, of the linked resource. +@property (retain) NSNumber *width; // unsignedIntValue + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityObjectAttachmentsItemThumbnailsItem +// + +@interface GTLPlusActivityObjectAttachmentsItemThumbnailsItem : GTLObject + +// Potential name of the thumbnail. +// Remapped to 'descriptionProperty' to avoid NSObject's 'description'. +@property (copy) NSString *descriptionProperty; + +// Image resource. +@property (retain) GTLPlusActivityObjectAttachmentsItemThumbnailsItemImage *image; + +// URL to the webpage containing the image. +@property (copy) NSString *url; + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityObjectAttachmentsItemThumbnailsItemImage +// + +@interface GTLPlusActivityObjectAttachmentsItemThumbnailsItemImage : GTLObject + +// The height, in pixels, of the linked resource. +@property (retain) NSNumber *height; // unsignedIntValue + +// Media type of the link. +@property (copy) NSString *type; + +// Image url. +@property (copy) NSString *url; + +// The width, in pixels, of the linked resource. +@property (retain) NSNumber *width; // unsignedIntValue + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusActivity.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusActivity.m new file mode 100644 index 0000000000..4d70f0173e --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusActivity.m @@ -0,0 +1,290 @@ +/* Copyright (c) 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLPlusActivity.m +// + +// ---------------------------------------------------------------------------- +// NOTE: This file is generated from Google APIs Discovery Service. +// Service: +// Google+ API (plus/v1) +// Description: +// The Google+ API enables developers to build on top of the Google+ platform. +// Documentation: +// https://developers.google.com/+/api/ +// Classes: +// GTLPlusActivity (0 custom class methods, 19 custom properties) +// GTLPlusActivityActor (0 custom class methods, 5 custom properties) +// GTLPlusActivityObject (0 custom class methods, 10 custom properties) +// GTLPlusActivityProvider (0 custom class methods, 1 custom properties) +// GTLPlusActivityActorImage (0 custom class methods, 1 custom properties) +// GTLPlusActivityActorName (0 custom class methods, 2 custom properties) +// GTLPlusActivityObjectActor (0 custom class methods, 4 custom properties) +// GTLPlusActivityObjectAttachmentsItem (0 custom class methods, 9 custom properties) +// GTLPlusActivityObjectPlusoners (0 custom class methods, 2 custom properties) +// GTLPlusActivityObjectReplies (0 custom class methods, 2 custom properties) +// GTLPlusActivityObjectResharers (0 custom class methods, 2 custom properties) +// GTLPlusActivityObjectActorImage (0 custom class methods, 1 custom properties) +// GTLPlusActivityObjectAttachmentsItemEmbed (0 custom class methods, 2 custom properties) +// GTLPlusActivityObjectAttachmentsItemFullImage (0 custom class methods, 4 custom properties) +// GTLPlusActivityObjectAttachmentsItemImage (0 custom class methods, 4 custom properties) +// GTLPlusActivityObjectAttachmentsItemThumbnailsItem (0 custom class methods, 3 custom properties) +// GTLPlusActivityObjectAttachmentsItemThumbnailsItemImage (0 custom class methods, 4 custom properties) + +#import "GTLPlusActivity.h" + +#import "GTLPlusAcl.h" + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivity +// + +@implementation GTLPlusActivity +@dynamic access, actor, address, annotation, crosspostSource, ETag, geocode, + identifier, kind, object, placeId, placeName, provider, published, + radius, title, updated, url, verb; + ++ (NSDictionary *)propertyToJSONKeyMap { + NSDictionary *map = + [NSDictionary dictionaryWithObjectsAndKeys: + @"etag", @"ETag", + @"id", @"identifier", + nil]; + return map; +} + ++ (void)load { + [self registerObjectClassForKind:@"plus#activity"]; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityActor +// + +@implementation GTLPlusActivityActor +@dynamic displayName, identifier, image, name, url; + ++ (NSDictionary *)propertyToJSONKeyMap { + NSDictionary *map = + [NSDictionary dictionaryWithObject:@"id" + forKey:@"identifier"]; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityObject +// + +@implementation GTLPlusActivityObject +@dynamic actor, attachments, content, identifier, objectType, originalContent, + plusoners, replies, resharers, url; + ++ (NSDictionary *)propertyToJSONKeyMap { + NSDictionary *map = + [NSDictionary dictionaryWithObject:@"id" + forKey:@"identifier"]; + return map; +} + ++ (NSDictionary *)arrayPropertyToClassMap { + NSDictionary *map = + [NSDictionary dictionaryWithObject:[GTLPlusActivityObjectAttachmentsItem class] + forKey:@"attachments"]; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityProvider +// + +@implementation GTLPlusActivityProvider +@dynamic title; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityActorImage +// + +@implementation GTLPlusActivityActorImage +@dynamic url; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityActorName +// + +@implementation GTLPlusActivityActorName +@dynamic familyName, givenName; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityObjectActor +// + +@implementation GTLPlusActivityObjectActor +@dynamic displayName, identifier, image, url; + ++ (NSDictionary *)propertyToJSONKeyMap { + NSDictionary *map = + [NSDictionary dictionaryWithObject:@"id" + forKey:@"identifier"]; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityObjectAttachmentsItem +// + +@implementation GTLPlusActivityObjectAttachmentsItem +@dynamic content, displayName, embed, fullImage, identifier, image, objectType, + thumbnails, url; + ++ (NSDictionary *)propertyToJSONKeyMap { + NSDictionary *map = + [NSDictionary dictionaryWithObject:@"id" + forKey:@"identifier"]; + return map; +} + ++ (NSDictionary *)arrayPropertyToClassMap { + NSDictionary *map = + [NSDictionary dictionaryWithObject:[GTLPlusActivityObjectAttachmentsItemThumbnailsItem class] + forKey:@"thumbnails"]; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityObjectPlusoners +// + +@implementation GTLPlusActivityObjectPlusoners +@dynamic selfLink, totalItems; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityObjectReplies +// + +@implementation GTLPlusActivityObjectReplies +@dynamic selfLink, totalItems; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityObjectResharers +// + +@implementation GTLPlusActivityObjectResharers +@dynamic selfLink, totalItems; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityObjectActorImage +// + +@implementation GTLPlusActivityObjectActorImage +@dynamic url; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityObjectAttachmentsItemEmbed +// + +@implementation GTLPlusActivityObjectAttachmentsItemEmbed +@dynamic type, url; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityObjectAttachmentsItemFullImage +// + +@implementation GTLPlusActivityObjectAttachmentsItemFullImage +@dynamic height, type, url, width; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityObjectAttachmentsItemImage +// + +@implementation GTLPlusActivityObjectAttachmentsItemImage +@dynamic height, type, url, width; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityObjectAttachmentsItemThumbnailsItem +// + +@implementation GTLPlusActivityObjectAttachmentsItemThumbnailsItem +@dynamic descriptionProperty, image, url; + ++ (NSDictionary *)propertyToJSONKeyMap { + NSDictionary *map = + [NSDictionary dictionaryWithObject:@"description" + forKey:@"descriptionProperty"]; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityObjectAttachmentsItemThumbnailsItemImage +// + +@implementation GTLPlusActivityObjectAttachmentsItemThumbnailsItemImage +@dynamic height, type, url, width; +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusActivityFeed.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusActivityFeed.h new file mode 100644 index 0000000000..f99ca32f91 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusActivityFeed.h @@ -0,0 +1,81 @@ +/* Copyright (c) 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLPlusActivityFeed.h +// + +// ---------------------------------------------------------------------------- +// NOTE: This file is generated from Google APIs Discovery Service. +// Service: +// Google+ API (plus/v1) +// Description: +// The Google+ API enables developers to build on top of the Google+ platform. +// Documentation: +// https://developers.google.com/+/api/ +// Classes: +// GTLPlusActivityFeed (0 custom class methods, 9 custom properties) + +#if GTL_BUILT_AS_FRAMEWORK + #import "GTL/GTLObject.h" +#else + #import "GTLObject.h" +#endif + +@class GTLPlusActivity; + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityFeed +// + +// This class supports NSFastEnumeration over its "items" property. It also +// supports -itemAtIndex: to retrieve individual objects from "items". + +@interface GTLPlusActivityFeed : GTLCollectionObject + +// ETag of this response for caching purposes. +@property (copy) NSString *ETag; + +// The ID of this collection of activities. Deprecated. +// identifier property maps to 'id' in JSON (to avoid Objective C's 'id'). +@property (copy) NSString *identifier; + +// The activities in this page of results. +@property (retain) NSArray *items; // of GTLPlusActivity + +// Identifies this resource as a collection of activities. Value: +// "plus#activityFeed". +@property (copy) NSString *kind; + +// Link to the next page of activities. +@property (copy) NSString *nextLink; + +// The continuation token, which is used to page through large result sets. +// Provide this value in a subsequent request to return the next page of +// results. +@property (copy) NSString *nextPageToken; + +// Link to this activity resource. +@property (copy) NSString *selfLink; + +// The title of this collection of activities. +@property (copy) NSString *title; + +// The time at which this collection of activities was last updated. Formatted +// as an RFC 3339 timestamp. +@property (retain) GTLDateTime *updated; + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusActivityFeed.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusActivityFeed.m new file mode 100644 index 0000000000..a3a34e4846 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusActivityFeed.m @@ -0,0 +1,64 @@ +/* Copyright (c) 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLPlusActivityFeed.m +// + +// ---------------------------------------------------------------------------- +// NOTE: This file is generated from Google APIs Discovery Service. +// Service: +// Google+ API (plus/v1) +// Description: +// The Google+ API enables developers to build on top of the Google+ platform. +// Documentation: +// https://developers.google.com/+/api/ +// Classes: +// GTLPlusActivityFeed (0 custom class methods, 9 custom properties) + +#import "GTLPlusActivityFeed.h" + +#import "GTLPlusActivity.h" + +// ---------------------------------------------------------------------------- +// +// GTLPlusActivityFeed +// + +@implementation GTLPlusActivityFeed +@dynamic ETag, identifier, items, kind, nextLink, nextPageToken, selfLink, + title, updated; + ++ (NSDictionary *)propertyToJSONKeyMap { + NSDictionary *map = + [NSDictionary dictionaryWithObjectsAndKeys: + @"etag", @"ETag", + @"id", @"identifier", + nil]; + return map; +} + ++ (NSDictionary *)arrayPropertyToClassMap { + NSDictionary *map = + [NSDictionary dictionaryWithObject:[GTLPlusActivity class] + forKey:@"items"]; + return map; +} + ++ (void)load { + [self registerObjectClassForKind:@"plus#activityFeed"]; +} + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusComment.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusComment.h new file mode 100644 index 0000000000..4698576ae0 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusComment.h @@ -0,0 +1,183 @@ +/* Copyright (c) 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLPlusComment.h +// + +// ---------------------------------------------------------------------------- +// NOTE: This file is generated from Google APIs Discovery Service. +// Service: +// Google+ API (plus/v1) +// Description: +// The Google+ API enables developers to build on top of the Google+ platform. +// Documentation: +// https://developers.google.com/+/api/ +// Classes: +// GTLPlusComment (0 custom class methods, 11 custom properties) +// GTLPlusCommentActor (0 custom class methods, 4 custom properties) +// GTLPlusCommentInReplyToItem (0 custom class methods, 2 custom properties) +// GTLPlusCommentObject (0 custom class methods, 3 custom properties) +// GTLPlusCommentPlusoners (0 custom class methods, 1 custom properties) +// GTLPlusCommentActorImage (0 custom class methods, 1 custom properties) + +#if GTL_BUILT_AS_FRAMEWORK + #import "GTL/GTLObject.h" +#else + #import "GTLObject.h" +#endif + +@class GTLPlusCommentActor; +@class GTLPlusCommentActorImage; +@class GTLPlusCommentInReplyToItem; +@class GTLPlusCommentObject; +@class GTLPlusCommentPlusoners; + +// ---------------------------------------------------------------------------- +// +// GTLPlusComment +// + +@interface GTLPlusComment : GTLObject + +// The person who posted this comment. +@property (retain) GTLPlusCommentActor *actor; + +// ETag of this response for caching purposes. +@property (copy) NSString *ETag; + +// The ID of this comment. +// identifier property maps to 'id' in JSON (to avoid Objective C's 'id'). +@property (copy) NSString *identifier; + +// The activity this comment replied to. +@property (retain) NSArray *inReplyTo; // of GTLPlusCommentInReplyToItem + +// Identifies this resource as a comment. Value: "plus#comment". +@property (copy) NSString *kind; + +// The object of this comment. +@property (retain) GTLPlusCommentObject *object; + +// People who +1'd this comment. +@property (retain) GTLPlusCommentPlusoners *plusoners; + +// The time at which this comment was initially published. Formatted as an RFC +// 3339 timestamp. +@property (retain) GTLDateTime *published; + +// Link to this comment resource. +@property (copy) NSString *selfLink; + +// The time at which this comment was last updated. Formatted as an RFC 3339 +// timestamp. +@property (retain) GTLDateTime *updated; + +// This comment's verb, indicating what action was performed. Possible values +// are: +// - "post" - Publish content to the stream. +@property (copy) NSString *verb; + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusCommentActor +// + +@interface GTLPlusCommentActor : GTLObject + +// The name of this actor, suitable for display. +@property (copy) NSString *displayName; + +// The ID of the actor. +// identifier property maps to 'id' in JSON (to avoid Objective C's 'id'). +@property (copy) NSString *identifier; + +// The image representation of this actor. +@property (retain) GTLPlusCommentActorImage *image; + +// A link to the person resource for this actor. +@property (copy) NSString *url; + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusCommentInReplyToItem +// + +@interface GTLPlusCommentInReplyToItem : GTLObject + +// The ID of the activity. +// identifier property maps to 'id' in JSON (to avoid Objective C's 'id'). +@property (copy) NSString *identifier; + +// The URL of the activity. +@property (copy) NSString *url; + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusCommentObject +// + +@interface GTLPlusCommentObject : GTLObject + +// The HTML-formatted content, suitable for display. +@property (copy) NSString *content; + +// The object type of this comment. Possible values are: +// - "comment" - A comment in reply to an activity. +@property (copy) NSString *objectType; + +// The content (text) as provided by the author, stored without any HTML +// formatting. When creating or updating a comment, this value must be supplied +// as plain text in the request. +@property (copy) NSString *originalContent; + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusCommentPlusoners +// + +@interface GTLPlusCommentPlusoners : GTLObject + +// Total number of people who +1'd this comment. +@property (retain) NSNumber *totalItems; // unsignedIntValue + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusCommentActorImage +// + +@interface GTLPlusCommentActorImage : GTLObject + +// The URL of the actor's profile photo. To re-size the image and crop it to a +// square, append the query string ?sz=x, where x is the dimension in pixels of +// each side. +@property (copy) NSString *url; + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusComment.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusComment.m new file mode 100644 index 0000000000..3abaa26d4f --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusComment.m @@ -0,0 +1,133 @@ +/* Copyright (c) 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLPlusComment.m +// + +// ---------------------------------------------------------------------------- +// NOTE: This file is generated from Google APIs Discovery Service. +// Service: +// Google+ API (plus/v1) +// Description: +// The Google+ API enables developers to build on top of the Google+ platform. +// Documentation: +// https://developers.google.com/+/api/ +// Classes: +// GTLPlusComment (0 custom class methods, 11 custom properties) +// GTLPlusCommentActor (0 custom class methods, 4 custom properties) +// GTLPlusCommentInReplyToItem (0 custom class methods, 2 custom properties) +// GTLPlusCommentObject (0 custom class methods, 3 custom properties) +// GTLPlusCommentPlusoners (0 custom class methods, 1 custom properties) +// GTLPlusCommentActorImage (0 custom class methods, 1 custom properties) + +#import "GTLPlusComment.h" + +// ---------------------------------------------------------------------------- +// +// GTLPlusComment +// + +@implementation GTLPlusComment +@dynamic actor, ETag, identifier, inReplyTo, kind, object, plusoners, published, + selfLink, updated, verb; + ++ (NSDictionary *)propertyToJSONKeyMap { + NSDictionary *map = + [NSDictionary dictionaryWithObjectsAndKeys: + @"etag", @"ETag", + @"id", @"identifier", + nil]; + return map; +} + ++ (NSDictionary *)arrayPropertyToClassMap { + NSDictionary *map = + [NSDictionary dictionaryWithObject:[GTLPlusCommentInReplyToItem class] + forKey:@"inReplyTo"]; + return map; +} + ++ (void)load { + [self registerObjectClassForKind:@"plus#comment"]; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusCommentActor +// + +@implementation GTLPlusCommentActor +@dynamic displayName, identifier, image, url; + ++ (NSDictionary *)propertyToJSONKeyMap { + NSDictionary *map = + [NSDictionary dictionaryWithObject:@"id" + forKey:@"identifier"]; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusCommentInReplyToItem +// + +@implementation GTLPlusCommentInReplyToItem +@dynamic identifier, url; + ++ (NSDictionary *)propertyToJSONKeyMap { + NSDictionary *map = + [NSDictionary dictionaryWithObject:@"id" + forKey:@"identifier"]; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusCommentObject +// + +@implementation GTLPlusCommentObject +@dynamic content, objectType, originalContent; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusCommentPlusoners +// + +@implementation GTLPlusCommentPlusoners +@dynamic totalItems; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusCommentActorImage +// + +@implementation GTLPlusCommentActorImage +@dynamic url; +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusCommentFeed.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusCommentFeed.h new file mode 100644 index 0000000000..74f9be5a65 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusCommentFeed.h @@ -0,0 +1,78 @@ +/* Copyright (c) 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLPlusCommentFeed.h +// + +// ---------------------------------------------------------------------------- +// NOTE: This file is generated from Google APIs Discovery Service. +// Service: +// Google+ API (plus/v1) +// Description: +// The Google+ API enables developers to build on top of the Google+ platform. +// Documentation: +// https://developers.google.com/+/api/ +// Classes: +// GTLPlusCommentFeed (0 custom class methods, 8 custom properties) + +#if GTL_BUILT_AS_FRAMEWORK + #import "GTL/GTLObject.h" +#else + #import "GTLObject.h" +#endif + +@class GTLPlusComment; + +// ---------------------------------------------------------------------------- +// +// GTLPlusCommentFeed +// + +// This class supports NSFastEnumeration over its "items" property. It also +// supports -itemAtIndex: to retrieve individual objects from "items". + +@interface GTLPlusCommentFeed : GTLCollectionObject + +// ETag of this response for caching purposes. +@property (copy) NSString *ETag; + +// The ID of this collection of comments. +// identifier property maps to 'id' in JSON (to avoid Objective C's 'id'). +@property (copy) NSString *identifier; + +// The comments in this page of results. +@property (retain) NSArray *items; // of GTLPlusComment + +// Identifies this resource as a collection of comments. Value: +// "plus#commentFeed". +@property (copy) NSString *kind; + +// Link to the next page of activities. +@property (copy) NSString *nextLink; + +// The continuation token, which is used to page through large result sets. +// Provide this value in a subsequent request to return the next page of +// results. +@property (copy) NSString *nextPageToken; + +// The title of this collection of comments. +@property (copy) NSString *title; + +// The time at which this collection of comments was last updated. Formatted as +// an RFC 3339 timestamp. +@property (retain) GTLDateTime *updated; + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusCommentFeed.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusCommentFeed.m new file mode 100644 index 0000000000..a8d81e6eab --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusCommentFeed.m @@ -0,0 +1,63 @@ +/* Copyright (c) 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLPlusCommentFeed.m +// + +// ---------------------------------------------------------------------------- +// NOTE: This file is generated from Google APIs Discovery Service. +// Service: +// Google+ API (plus/v1) +// Description: +// The Google+ API enables developers to build on top of the Google+ platform. +// Documentation: +// https://developers.google.com/+/api/ +// Classes: +// GTLPlusCommentFeed (0 custom class methods, 8 custom properties) + +#import "GTLPlusCommentFeed.h" + +#import "GTLPlusComment.h" + +// ---------------------------------------------------------------------------- +// +// GTLPlusCommentFeed +// + +@implementation GTLPlusCommentFeed +@dynamic ETag, identifier, items, kind, nextLink, nextPageToken, title, updated; + ++ (NSDictionary *)propertyToJSONKeyMap { + NSDictionary *map = + [NSDictionary dictionaryWithObjectsAndKeys: + @"etag", @"ETag", + @"id", @"identifier", + nil]; + return map; +} + ++ (NSDictionary *)arrayPropertyToClassMap { + NSDictionary *map = + [NSDictionary dictionaryWithObject:[GTLPlusComment class] + forKey:@"items"]; + return map; +} + ++ (void)load { + [self registerObjectClassForKind:@"plus#commentFeed"]; +} + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusConstants.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusConstants.h new file mode 100644 index 0000000000..b5e87ad7fc --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusConstants.h @@ -0,0 +1,57 @@ +/* Copyright (c) 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLPlusConstants.h +// + +// ---------------------------------------------------------------------------- +// NOTE: This file is generated from Google APIs Discovery Service. +// Service: +// Google+ API (plus/v1) +// Description: +// The Google+ API enables developers to build on top of the Google+ platform. +// Documentation: +// https://developers.google.com/+/api/ + +#import + +#if GTL_BUILT_AS_FRAMEWORK + #import "GTL/GTLDefines.h" +#else + #import "GTLDefines.h" +#endif + +// Authorization scope +// Know your name, basic info, and list of people you're connected to on Google+ +GTL_EXTERN NSString * const kGTLAuthScopePlusLogin; // "https://www.googleapis.com/auth/plus.login" +// Know who you are on Google +GTL_EXTERN NSString * const kGTLAuthScopePlusMe; // "https://www.googleapis.com/auth/plus.me" + +// Collection +GTL_EXTERN NSString * const kGTLPlusCollectionPlusoners; // "plusoners" +GTL_EXTERN NSString * const kGTLPlusCollectionPublic; // "public" +GTL_EXTERN NSString * const kGTLPlusCollectionResharers; // "resharers" +GTL_EXTERN NSString * const kGTLPlusCollectionVault; // "vault" +GTL_EXTERN NSString * const kGTLPlusCollectionVisible; // "visible" + +// OrderBy +GTL_EXTERN NSString * const kGTLPlusOrderByAlphabetical; // "alphabetical" +GTL_EXTERN NSString * const kGTLPlusOrderByBest; // "best" +GTL_EXTERN NSString * const kGTLPlusOrderByRecent; // "recent" + +// SortOrder +GTL_EXTERN NSString * const kGTLPlusSortOrderAscending; // "ascending" +GTL_EXTERN NSString * const kGTLPlusSortOrderDescending; // "descending" diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusConstants.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusConstants.m new file mode 100644 index 0000000000..bb5610ff4b --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusConstants.m @@ -0,0 +1,49 @@ +/* Copyright (c) 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLPlusConstants.m +// + +// ---------------------------------------------------------------------------- +// NOTE: This file is generated from Google APIs Discovery Service. +// Service: +// Google+ API (plus/v1) +// Description: +// The Google+ API enables developers to build on top of the Google+ platform. +// Documentation: +// https://developers.google.com/+/api/ + +#import "GTLPlusConstants.h" + +// Authorization scope +NSString * const kGTLAuthScopePlusLogin = @"https://www.googleapis.com/auth/plus.login"; +NSString * const kGTLAuthScopePlusMe = @"https://www.googleapis.com/auth/plus.me"; + +// Collection +NSString * const kGTLPlusCollectionPlusoners = @"plusoners"; +NSString * const kGTLPlusCollectionPublic = @"public"; +NSString * const kGTLPlusCollectionResharers = @"resharers"; +NSString * const kGTLPlusCollectionVault = @"vault"; +NSString * const kGTLPlusCollectionVisible = @"visible"; + +// OrderBy +NSString * const kGTLPlusOrderByAlphabetical = @"alphabetical"; +NSString * const kGTLPlusOrderByBest = @"best"; +NSString * const kGTLPlusOrderByRecent = @"recent"; + +// SortOrder +NSString * const kGTLPlusSortOrderAscending = @"ascending"; +NSString * const kGTLPlusSortOrderDescending = @"descending"; diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusItemScope.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusItemScope.h new file mode 100644 index 0000000000..17e2b3718f --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusItemScope.h @@ -0,0 +1,225 @@ +/* Copyright (c) 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLPlusItemScope.h +// + +// ---------------------------------------------------------------------------- +// NOTE: This file is generated from Google APIs Discovery Service. +// Service: +// Google+ API (plus/v1) +// Description: +// The Google+ API enables developers to build on top of the Google+ platform. +// Documentation: +// https://developers.google.com/+/api/ +// Classes: +// GTLPlusItemScope (0 custom class methods, 55 custom properties) + +#if GTL_BUILT_AS_FRAMEWORK + #import "GTL/GTLObject.h" +#else + #import "GTLObject.h" +#endif + +@class GTLPlusItemScope; + +// ---------------------------------------------------------------------------- +// +// GTLPlusItemScope +// + +@interface GTLPlusItemScope : GTLObject + +// The subject matter of the content. +@property (retain) GTLPlusItemScope *about; + +// An additional name for a Person, can be used for a middle name. +@property (retain) NSArray *additionalName; // of NSString + +// Postal address. +@property (retain) GTLPlusItemScope *address; + +// Address country. +@property (copy) NSString *addressCountry; + +// Address locality. +@property (copy) NSString *addressLocality; + +// Address region. +@property (copy) NSString *addressRegion; + +// The encoding. +@property (retain) NSArray *associatedMedia; // of GTLPlusItemScope + +// Number of attendees. +@property (retain) NSNumber *attendeeCount; // intValue + +// A person attending the event. +@property (retain) NSArray *attendees; // of GTLPlusItemScope + +// From http://schema.org/MusicRecording, the audio file. +@property (retain) GTLPlusItemScope *audio; + +// The person who created this scope. +@property (retain) NSArray *author; // of GTLPlusItemScope + +// Best possible rating value. +@property (copy) NSString *bestRating; + +// Date of birth. +@property (copy) NSString *birthDate; + +// From http://schema.org/MusicRecording, the artist that performed this +// recording. +@property (retain) GTLPlusItemScope *byArtist; + +// The caption for this object. +@property (copy) NSString *caption; + +// File size in (mega/kilo) bytes. +@property (copy) NSString *contentSize; + +// Actual bytes of the media object, for example the image file or video file. +@property (copy) NSString *contentUrl; + +// The list of contributors for this scope. +@property (retain) NSArray *contributor; // of GTLPlusItemScope + +// The date this scope was created. +@property (copy) NSString *dateCreated; + +// The date this scope was last modified. +@property (copy) NSString *dateModified; + +// The initial date this scope was published. +@property (copy) NSString *datePublished; + +// The string describing the content of this scope. +// Remapped to 'descriptionProperty' to avoid NSObject's 'description'. +@property (copy) NSString *descriptionProperty; + +// The duration of the item (movie, audio recording, event, etc.) in ISO 8601 +// date format. +@property (copy) NSString *duration; + +// A URL pointing to a player for a specific video. In general, this is the +// information in the src element of an embed tag and should not be the same as +// the content of the loc tag. +@property (copy) NSString *embedUrl; + +// The end date and time of the event (in ISO 8601 date format). +@property (copy) NSString *endDate; + +// Family name. In the U.S., the last name of an Person. This can be used along +// with givenName instead of the Name property. +@property (copy) NSString *familyName; + +// Gender of the person. +@property (copy) NSString *gender; + +// Geo coordinates. +@property (retain) GTLPlusItemScope *geo; + +// Given name. In the U.S., the first name of a Person. This can be used along +// with familyName instead of the Name property. +@property (copy) NSString *givenName; + +// The height of the media object. +@property (copy) NSString *height; + +// The id for this item scope. +// identifier property maps to 'id' in JSON (to avoid Objective C's 'id'). +@property (copy) NSString *identifier; + +// A url to the image for this scope. +@property (copy) NSString *image; + +// From http://schema.org/MusicRecording, which album a song is in. +@property (retain) GTLPlusItemScope *inAlbum; + +// Identifies this resource as an itemScope. +@property (copy) NSString *kind; + +// Latitude. +@property (retain) NSNumber *latitude; // doubleValue + +// The location of the event or organization. +@property (retain) GTLPlusItemScope *location; + +// Longitude. +@property (retain) NSNumber *longitude; // doubleValue + +// The name of this scope. +@property (copy) NSString *name; + +// Property of http://schema.org/TVEpisode indicating which series the episode +// belongs to. +@property (retain) GTLPlusItemScope *partOfTVSeries; + +// The main performer or performers of the event-for example, a presenter, +// musician, or actor. +@property (retain) NSArray *performers; // of GTLPlusItemScope + +// Player type required-for example, Flash or Silverlight. +@property (copy) NSString *playerType; + +// Postal code. +@property (copy) NSString *postalCode; + +// Post office box number. +@property (copy) NSString *postOfficeBoxNumber; + +// Rating value. +@property (copy) NSString *ratingValue; + +// Review rating. +@property (retain) GTLPlusItemScope *reviewRating; + +// The start date and time of the event (in ISO 8601 date format). +@property (copy) NSString *startDate; + +// Street address. +@property (copy) NSString *streetAddress; + +// Comment text, review text, etc. +@property (copy) NSString *text; + +// Thumbnail image for an image or video. +@property (retain) GTLPlusItemScope *thumbnail; + +// A url to a thumbnail image for this scope. +@property (copy) NSString *thumbnailUrl; + +// The exchange traded instrument associated with a Corporation object. The +// tickerSymbol is expressed as an exchange and an instrument name separated by +// a space character. For the exchange component of the tickerSymbol attribute, +// we reccommend using the controlled vocaulary of Market Identifier Codes (MIC) +// specified in ISO15022. +@property (copy) NSString *tickerSymbol; + +// The item type. +@property (copy) NSString *type; + +// A URL for the item upon which the action was performed. +@property (copy) NSString *url; + +// The width of the media object. +@property (copy) NSString *width; + +// Worst possible rating value. +@property (copy) NSString *worstRating; + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusItemScope.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusItemScope.m new file mode 100644 index 0000000000..ee7615037b --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusItemScope.m @@ -0,0 +1,77 @@ +/* Copyright (c) 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLPlusItemScope.m +// + +// ---------------------------------------------------------------------------- +// NOTE: This file is generated from Google APIs Discovery Service. +// Service: +// Google+ API (plus/v1) +// Description: +// The Google+ API enables developers to build on top of the Google+ platform. +// Documentation: +// https://developers.google.com/+/api/ +// Classes: +// GTLPlusItemScope (0 custom class methods, 55 custom properties) + +#import "GTLPlusItemScope.h" + +// ---------------------------------------------------------------------------- +// +// GTLPlusItemScope +// + +@implementation GTLPlusItemScope +@dynamic about, additionalName, address, addressCountry, addressLocality, + addressRegion, associatedMedia, attendeeCount, attendees, audio, + author, bestRating, birthDate, byArtist, caption, contentSize, + contentUrl, contributor, dateCreated, dateModified, datePublished, + descriptionProperty, duration, embedUrl, endDate, familyName, gender, + geo, givenName, height, identifier, image, inAlbum, kind, latitude, + location, longitude, name, partOfTVSeries, performers, playerType, + postalCode, postOfficeBoxNumber, ratingValue, reviewRating, startDate, + streetAddress, text, thumbnail, thumbnailUrl, tickerSymbol, type, url, + width, worstRating; + ++ (NSDictionary *)propertyToJSONKeyMap { + NSDictionary *map = + [NSDictionary dictionaryWithObjectsAndKeys: + @"associated_media", @"associatedMedia", + @"description", @"descriptionProperty", + @"id", @"identifier", + nil]; + return map; +} + ++ (NSDictionary *)arrayPropertyToClassMap { + NSDictionary *map = + [NSDictionary dictionaryWithObjectsAndKeys: + [NSString class], @"additionalName", + [GTLPlusItemScope class], @"associated_media", + [GTLPlusItemScope class], @"attendees", + [GTLPlusItemScope class], @"author", + [GTLPlusItemScope class], @"contributor", + [GTLPlusItemScope class], @"performers", + nil]; + return map; +} + ++ (void)load { + [self registerObjectClassForKind:@"plus#itemScope"]; +} + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusMoment.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusMoment.h new file mode 100644 index 0000000000..0b028c8861 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusMoment.h @@ -0,0 +1,65 @@ +/* Copyright (c) 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLPlusMoment.h +// + +// ---------------------------------------------------------------------------- +// NOTE: This file is generated from Google APIs Discovery Service. +// Service: +// Google+ API (plus/v1) +// Description: +// The Google+ API enables developers to build on top of the Google+ platform. +// Documentation: +// https://developers.google.com/+/api/ +// Classes: +// GTLPlusMoment (0 custom class methods, 6 custom properties) + +#if GTL_BUILT_AS_FRAMEWORK + #import "GTL/GTLObject.h" +#else + #import "GTLObject.h" +#endif + +@class GTLPlusItemScope; + +// ---------------------------------------------------------------------------- +// +// GTLPlusMoment +// + +@interface GTLPlusMoment : GTLObject + +// The moment ID. +// identifier property maps to 'id' in JSON (to avoid Objective C's 'id'). +@property (copy) NSString *identifier; + +// Identifies this resource as a moment. +@property (copy) NSString *kind; + +// The object generated by performing the action on the item +@property (retain) GTLPlusItemScope *result; + +// Time stamp of when the action occurred in RFC3339 format. +@property (retain) GTLDateTime *startDate; + +// The object on which the action was performed. +@property (retain) GTLPlusItemScope *target; + +// The schema.org activity type. +@property (copy) NSString *type; + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusMoment.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusMoment.m new file mode 100644 index 0000000000..7785726a04 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusMoment.m @@ -0,0 +1,54 @@ +/* Copyright (c) 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLPlusMoment.m +// + +// ---------------------------------------------------------------------------- +// NOTE: This file is generated from Google APIs Discovery Service. +// Service: +// Google+ API (plus/v1) +// Description: +// The Google+ API enables developers to build on top of the Google+ platform. +// Documentation: +// https://developers.google.com/+/api/ +// Classes: +// GTLPlusMoment (0 custom class methods, 6 custom properties) + +#import "GTLPlusMoment.h" + +#import "GTLPlusItemScope.h" + +// ---------------------------------------------------------------------------- +// +// GTLPlusMoment +// + +@implementation GTLPlusMoment +@dynamic identifier, kind, result, startDate, target, type; + ++ (NSDictionary *)propertyToJSONKeyMap { + NSDictionary *map = + [NSDictionary dictionaryWithObject:@"id" + forKey:@"identifier"]; + return map; +} + ++ (void)load { + [self registerObjectClassForKind:@"plus#moment"]; +} + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusMomentsFeed.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusMomentsFeed.h new file mode 100644 index 0000000000..6cc8106b09 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusMomentsFeed.h @@ -0,0 +1,76 @@ +/* Copyright (c) 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLPlusMomentsFeed.h +// + +// ---------------------------------------------------------------------------- +// NOTE: This file is generated from Google APIs Discovery Service. +// Service: +// Google+ API (plus/v1) +// Description: +// The Google+ API enables developers to build on top of the Google+ platform. +// Documentation: +// https://developers.google.com/+/api/ +// Classes: +// GTLPlusMomentsFeed (0 custom class methods, 8 custom properties) + +#if GTL_BUILT_AS_FRAMEWORK + #import "GTL/GTLObject.h" +#else + #import "GTLObject.h" +#endif + +@class GTLPlusMoment; + +// ---------------------------------------------------------------------------- +// +// GTLPlusMomentsFeed +// + +// This class supports NSFastEnumeration over its "items" property. It also +// supports -itemAtIndex: to retrieve individual objects from "items". + +@interface GTLPlusMomentsFeed : GTLCollectionObject + +// ETag of this response for caching purposes. +@property (copy) NSString *ETag; + +// The moments in this page of results. +@property (retain) NSArray *items; // of GTLPlusMoment + +// Identifies this resource as a collection of moments. Value: +// "plus#momentsFeed". +@property (copy) NSString *kind; + +// Link to the next page of moments. +@property (copy) NSString *nextLink; + +// The continuation token, which is used to page through large result sets. +// Provide this value in a subsequent request to return the next page of +// results. +@property (copy) NSString *nextPageToken; + +// Link to this page of moments. +@property (copy) NSString *selfLink; + +// The title of this collection of moments. +@property (copy) NSString *title; + +// The RFC 339 timestamp for when this collection of moments was last updated. +@property (retain) GTLDateTime *updated; + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusMomentsFeed.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusMomentsFeed.m new file mode 100644 index 0000000000..21ff97c5c7 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusMomentsFeed.m @@ -0,0 +1,61 @@ +/* Copyright (c) 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLPlusMomentsFeed.m +// + +// ---------------------------------------------------------------------------- +// NOTE: This file is generated from Google APIs Discovery Service. +// Service: +// Google+ API (plus/v1) +// Description: +// The Google+ API enables developers to build on top of the Google+ platform. +// Documentation: +// https://developers.google.com/+/api/ +// Classes: +// GTLPlusMomentsFeed (0 custom class methods, 8 custom properties) + +#import "GTLPlusMomentsFeed.h" + +#import "GTLPlusMoment.h" + +// ---------------------------------------------------------------------------- +// +// GTLPlusMomentsFeed +// + +@implementation GTLPlusMomentsFeed +@dynamic ETag, items, kind, nextLink, nextPageToken, selfLink, title, updated; + ++ (NSDictionary *)propertyToJSONKeyMap { + NSDictionary *map = + [NSDictionary dictionaryWithObject:@"etag" + forKey:@"ETag"]; + return map; +} + ++ (NSDictionary *)arrayPropertyToClassMap { + NSDictionary *map = + [NSDictionary dictionaryWithObject:[GTLPlusMoment class] + forKey:@"items"]; + return map; +} + ++ (void)load { + [self registerObjectClassForKind:@"plus#momentsFeed"]; +} + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusPeopleFeed.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusPeopleFeed.h new file mode 100644 index 0000000000..523afadd33 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusPeopleFeed.h @@ -0,0 +1,76 @@ +/* Copyright (c) 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLPlusPeopleFeed.h +// + +// ---------------------------------------------------------------------------- +// NOTE: This file is generated from Google APIs Discovery Service. +// Service: +// Google+ API (plus/v1) +// Description: +// The Google+ API enables developers to build on top of the Google+ platform. +// Documentation: +// https://developers.google.com/+/api/ +// Classes: +// GTLPlusPeopleFeed (0 custom class methods, 7 custom properties) + +#if GTL_BUILT_AS_FRAMEWORK + #import "GTL/GTLObject.h" +#else + #import "GTLObject.h" +#endif + +@class GTLPlusPerson; + +// ---------------------------------------------------------------------------- +// +// GTLPlusPeopleFeed +// + +// This class supports NSFastEnumeration over its "items" property. It also +// supports -itemAtIndex: to retrieve individual objects from "items". + +@interface GTLPlusPeopleFeed : GTLCollectionObject + +// ETag of this response for caching purposes. +@property (copy) NSString *ETag; + +// The people in this page of results. Each item includes the id, displayName, +// image, and url for the person. To retrieve additional profile data, see the +// people.get method. +@property (retain) NSArray *items; // of GTLPlusPerson + +// Identifies this resource as a collection of people. Value: "plus#peopleFeed". +@property (copy) NSString *kind; + +// The continuation token, which is used to page through large result sets. +// Provide this value in a subsequent request to return the next page of +// results. +@property (copy) NSString *nextPageToken; + +// Link to this resource. +@property (copy) NSString *selfLink; + +// The title of this collection of people. +@property (copy) NSString *title; + +// The total number of people available in this list. The number of people in a +// response might be smaller due to paging. This might not be set for all +// collections. +@property (retain) NSNumber *totalItems; // intValue + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusPeopleFeed.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusPeopleFeed.m new file mode 100644 index 0000000000..4861f65f73 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusPeopleFeed.m @@ -0,0 +1,61 @@ +/* Copyright (c) 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLPlusPeopleFeed.m +// + +// ---------------------------------------------------------------------------- +// NOTE: This file is generated from Google APIs Discovery Service. +// Service: +// Google+ API (plus/v1) +// Description: +// The Google+ API enables developers to build on top of the Google+ platform. +// Documentation: +// https://developers.google.com/+/api/ +// Classes: +// GTLPlusPeopleFeed (0 custom class methods, 7 custom properties) + +#import "GTLPlusPeopleFeed.h" + +#import "GTLPlusPerson.h" + +// ---------------------------------------------------------------------------- +// +// GTLPlusPeopleFeed +// + +@implementation GTLPlusPeopleFeed +@dynamic ETag, items, kind, nextPageToken, selfLink, title, totalItems; + ++ (NSDictionary *)propertyToJSONKeyMap { + NSDictionary *map = + [NSDictionary dictionaryWithObject:@"etag" + forKey:@"ETag"]; + return map; +} + ++ (NSDictionary *)arrayPropertyToClassMap { + NSDictionary *map = + [NSDictionary dictionaryWithObject:[GTLPlusPerson class] + forKey:@"items"]; + return map; +} + ++ (void)load { + [self registerObjectClassForKind:@"plus#peopleFeed"]; +} + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusPerson.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusPerson.h new file mode 100644 index 0000000000..57ae94ed0e --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusPerson.h @@ -0,0 +1,388 @@ +/* Copyright (c) 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLPlusPerson.h +// + +// ---------------------------------------------------------------------------- +// NOTE: This file is generated from Google APIs Discovery Service. +// Service: +// Google+ API (plus/v1) +// Description: +// The Google+ API enables developers to build on top of the Google+ platform. +// Documentation: +// https://developers.google.com/+/api/ +// Classes: +// GTLPlusPerson (0 custom class methods, 28 custom properties) +// GTLPlusPersonAgeRange (0 custom class methods, 2 custom properties) +// GTLPlusPersonCover (0 custom class methods, 3 custom properties) +// GTLPlusPersonEmailsItem (0 custom class methods, 3 custom properties) +// GTLPlusPersonImage (0 custom class methods, 1 custom properties) +// GTLPlusPersonName (0 custom class methods, 6 custom properties) +// GTLPlusPersonOrganizationsItem (0 custom class methods, 9 custom properties) +// GTLPlusPersonPlacesLivedItem (0 custom class methods, 2 custom properties) +// GTLPlusPersonUrlsItem (0 custom class methods, 3 custom properties) +// GTLPlusPersonCoverCoverInfo (0 custom class methods, 2 custom properties) +// GTLPlusPersonCoverCoverPhoto (0 custom class methods, 3 custom properties) + +#if GTL_BUILT_AS_FRAMEWORK + #import "GTL/GTLObject.h" +#else + #import "GTLObject.h" +#endif + +@class GTLPlusPersonAgeRange; +@class GTLPlusPersonCover; +@class GTLPlusPersonCoverCoverInfo; +@class GTLPlusPersonCoverCoverPhoto; +@class GTLPlusPersonEmailsItem; +@class GTLPlusPersonImage; +@class GTLPlusPersonName; +@class GTLPlusPersonOrganizationsItem; +@class GTLPlusPersonPlacesLivedItem; +@class GTLPlusPersonUrlsItem; + +// ---------------------------------------------------------------------------- +// +// GTLPlusPerson +// + +@interface GTLPlusPerson : GTLObject + +// A short biography for this person. +@property (copy) NSString *aboutMe; + +// The age range of the person. +@property (retain) GTLPlusPersonAgeRange *ageRange; + +// The person's date of birth, represented as YYYY-MM-DD. +@property (copy) NSString *birthday; + +// The "bragging rights" line of this person. +@property (copy) NSString *braggingRights; + +// If a Google+ Page and for followers who are visible, the number of people who +// have added this page to a circle. +@property (retain) NSNumber *circledByCount; // intValue + +// The cover photo content. +@property (retain) GTLPlusPersonCover *cover; + +// The current location for this person. +@property (copy) NSString *currentLocation; + +// The name of this person, suitable for display. +@property (copy) NSString *displayName; + +// A list of email addresses for this person. +@property (retain) NSArray *emails; // of GTLPlusPersonEmailsItem + +// ETag of this response for caching purposes. +@property (copy) NSString *ETag; + +// The person's gender. Possible values are: +// - "male" - Male gender. +// - "female" - Female gender. +// - "other" - Other. +@property (copy) NSString *gender; + +// If "true", indicates that the person has installed the app that is making the +// request and has chosen to expose this install state to the caller. A value of +// "false" indicates that the install state cannot be determined (it is either +// not installed or the person has chosen to keep this information private). +@property (retain) NSNumber *hasApp; // boolValue + +// The ID of this person. +// identifier property maps to 'id' in JSON (to avoid Objective C's 'id'). +@property (copy) NSString *identifier; + +// The representation of the person's profile photo. +@property (retain) GTLPlusPersonImage *image; + +// Whether this user has signed up for Google+. +@property (retain) NSNumber *isPlusUser; // boolValue + +// Identifies this resource as a person. Value: "plus#person". +@property (copy) NSString *kind; + +// The user's preferred language for rendering. +@property (copy) NSString *language; + +// An object representation of the individual components of a person's name. +@property (retain) GTLPlusPersonName *name; + +// The nickname of this person. +@property (copy) NSString *nickname; + +// Type of person within Google+. Possible values are: +// - "person" - represents an actual person. +// - "page" - represents a page. +@property (copy) NSString *objectType; + +// A list of current or past organizations with which this person is associated. +@property (retain) NSArray *organizations; // of GTLPlusPersonOrganizationsItem + +// A list of places where this person has lived. +@property (retain) NSArray *placesLived; // of GTLPlusPersonPlacesLivedItem + +// If a Google+ Page, the number of people who have +1'ed this page. +@property (retain) NSNumber *plusOneCount; // intValue + +// The person's relationship status. Possible values are: +// - "single" - Person is single. +// - "in_a_relationship" - Person is in a relationship. +// - "engaged" - Person is engaged. +// - "married" - Person is married. +// - "its_complicated" - The relationship is complicated. +// - "open_relationship" - Person is in an open relationship. +// - "widowed" - Person is widowed. +// - "in_domestic_partnership" - Person is in a domestic partnership. +// - "in_civil_union" - Person is in a civil union. +@property (copy) NSString *relationshipStatus; + +// The brief description (tagline) of this person. +@property (copy) NSString *tagline; + +// The URL of this person's profile. +@property (copy) NSString *url; + +// A list of URLs for this person. +@property (retain) NSArray *urls; // of GTLPlusPersonUrlsItem + +// Whether the person or Google+ Page has been verified. +@property (retain) NSNumber *verified; // boolValue + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusPersonAgeRange +// + +@interface GTLPlusPersonAgeRange : GTLObject + +// The age range's upper bound, if any. +@property (retain) NSNumber *max; // intValue + +// The age range's lower bound, if any. +@property (retain) NSNumber *min; // intValue + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusPersonCover +// + +@interface GTLPlusPersonCover : GTLObject + +// Extra information about the cover photo. +@property (retain) GTLPlusPersonCoverCoverInfo *coverInfo; + +// The person's primary cover image. +@property (retain) GTLPlusPersonCoverCoverPhoto *coverPhoto; + +// The layout of the cover art. Possible values are: +// - "banner" - One large image banner. +@property (copy) NSString *layout; + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusPersonEmailsItem +// + +@interface GTLPlusPersonEmailsItem : GTLObject + +// If "true", indicates this email address is the person's primary one. +@property (retain) NSNumber *primary; // boolValue + +// The type of address. Possible values are: +// - "home" - Home email address. +// - "work" - Work email address. +// - "other" - Other. +@property (copy) NSString *type; + +// The email address. +@property (copy) NSString *value; + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusPersonImage +// + +@interface GTLPlusPersonImage : GTLObject + +// The URL of the person's profile photo. To re-size the image and crop it to a +// square, append the query string ?sz=x, where x is the dimension in pixels of +// each side. +@property (copy) NSString *url; + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusPersonName +// + +@interface GTLPlusPersonName : GTLObject + +// The family name (last name) of this person. +@property (copy) NSString *familyName; + +// The full name of this person, including middle names, suffixes, etc. +@property (copy) NSString *formatted; + +// The given name (first name) of this person. +@property (copy) NSString *givenName; + +// The honorific prefixes (such as "Dr." or "Mrs.") for this person. +@property (copy) NSString *honorificPrefix; + +// The honorific suffixes (such as "Jr.") for this person. +@property (copy) NSString *honorificSuffix; + +// The middle name of this person. +@property (copy) NSString *middleName; + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusPersonOrganizationsItem +// + +@interface GTLPlusPersonOrganizationsItem : GTLObject + +// The department within the organization. Deprecated. +@property (copy) NSString *department; + +// A short description of the person's role in this organization. Deprecated. +// Remapped to 'descriptionProperty' to avoid NSObject's 'description'. +@property (copy) NSString *descriptionProperty; + +// The date the person left this organization. +@property (copy) NSString *endDate; + +// The location of this organization. Deprecated. +@property (copy) NSString *location; + +// The name of the organization. +@property (copy) NSString *name; + +// If "true", indicates this organization is the person's primary one (typically +// interpreted as current one). +@property (retain) NSNumber *primary; // boolValue + +// The date the person joined this organization. +@property (copy) NSString *startDate; + +// The person's job title or role within the organization. +@property (copy) NSString *title; + +// The type of organization. Possible values are: +// - "work" - Work. +// - "school" - School. +@property (copy) NSString *type; + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusPersonPlacesLivedItem +// + +@interface GTLPlusPersonPlacesLivedItem : GTLObject + +// If "true", this place of residence is this person's primary residence. +@property (retain) NSNumber *primary; // boolValue + +// A place where this person has lived. For example: "Seattle, WA", "Near +// Toronto". +@property (copy) NSString *value; + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusPersonUrlsItem +// + +@interface GTLPlusPersonUrlsItem : GTLObject + +// If "true", this URL is the person's primary URL. +@property (retain) NSNumber *primary; // boolValue + +// The type of URL. Possible values are: +// - "home" - URL for home. +// - "work" - URL for work. +// - "blog" - URL for blog. +// - "profile" - URL for profile. +// - "other" - Other. +@property (copy) NSString *type; + +// The URL value. +@property (copy) NSString *value; + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusPersonCoverCoverInfo +// + +@interface GTLPlusPersonCoverCoverInfo : GTLObject + +// The difference between the left position of the image cover and the actual +// displayed cover image. Only valid for BANNER layout. +@property (retain) NSNumber *leftImageOffset; // intValue + +// The difference between the top position of the image cover and the actual +// displayed cover image. Only valid for BANNER layout. +@property (retain) NSNumber *topImageOffset; // intValue + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusPersonCoverCoverPhoto +// + +@interface GTLPlusPersonCoverCoverPhoto : GTLObject + +// The height to the image. +@property (retain) NSNumber *height; // intValue + +// The url to the image. +@property (copy) NSString *url; + +// The width to the image. +@property (retain) NSNumber *width; // intValue + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusPerson.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusPerson.m new file mode 100644 index 0000000000..200434fb0a --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLPlusPerson.m @@ -0,0 +1,189 @@ +/* Copyright (c) 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLPlusPerson.m +// + +// ---------------------------------------------------------------------------- +// NOTE: This file is generated from Google APIs Discovery Service. +// Service: +// Google+ API (plus/v1) +// Description: +// The Google+ API enables developers to build on top of the Google+ platform. +// Documentation: +// https://developers.google.com/+/api/ +// Classes: +// GTLPlusPerson (0 custom class methods, 28 custom properties) +// GTLPlusPersonAgeRange (0 custom class methods, 2 custom properties) +// GTLPlusPersonCover (0 custom class methods, 3 custom properties) +// GTLPlusPersonEmailsItem (0 custom class methods, 3 custom properties) +// GTLPlusPersonImage (0 custom class methods, 1 custom properties) +// GTLPlusPersonName (0 custom class methods, 6 custom properties) +// GTLPlusPersonOrganizationsItem (0 custom class methods, 9 custom properties) +// GTLPlusPersonPlacesLivedItem (0 custom class methods, 2 custom properties) +// GTLPlusPersonUrlsItem (0 custom class methods, 3 custom properties) +// GTLPlusPersonCoverCoverInfo (0 custom class methods, 2 custom properties) +// GTLPlusPersonCoverCoverPhoto (0 custom class methods, 3 custom properties) + +#import "GTLPlusPerson.h" + +// ---------------------------------------------------------------------------- +// +// GTLPlusPerson +// + +@implementation GTLPlusPerson +@dynamic aboutMe, ageRange, birthday, braggingRights, circledByCount, cover, + currentLocation, displayName, emails, ETag, gender, hasApp, identifier, + image, isPlusUser, kind, language, name, nickname, objectType, + organizations, placesLived, plusOneCount, relationshipStatus, tagline, + url, urls, verified; + ++ (NSDictionary *)propertyToJSONKeyMap { + NSDictionary *map = + [NSDictionary dictionaryWithObjectsAndKeys: + @"etag", @"ETag", + @"id", @"identifier", + nil]; + return map; +} + ++ (NSDictionary *)arrayPropertyToClassMap { + NSDictionary *map = + [NSDictionary dictionaryWithObjectsAndKeys: + [GTLPlusPersonEmailsItem class], @"emails", + [GTLPlusPersonOrganizationsItem class], @"organizations", + [GTLPlusPersonPlacesLivedItem class], @"placesLived", + [GTLPlusPersonUrlsItem class], @"urls", + nil]; + return map; +} + ++ (void)load { + [self registerObjectClassForKind:@"plus#person"]; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusPersonAgeRange +// + +@implementation GTLPlusPersonAgeRange +@dynamic max, min; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusPersonCover +// + +@implementation GTLPlusPersonCover +@dynamic coverInfo, coverPhoto, layout; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusPersonEmailsItem +// + +@implementation GTLPlusPersonEmailsItem +@dynamic primary, type, value; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusPersonImage +// + +@implementation GTLPlusPersonImage +@dynamic url; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusPersonName +// + +@implementation GTLPlusPersonName +@dynamic familyName, formatted, givenName, honorificPrefix, honorificSuffix, + middleName; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusPersonOrganizationsItem +// + +@implementation GTLPlusPersonOrganizationsItem +@dynamic department, descriptionProperty, endDate, location, name, primary, + startDate, title, type; + ++ (NSDictionary *)propertyToJSONKeyMap { + NSDictionary *map = + [NSDictionary dictionaryWithObject:@"description" + forKey:@"descriptionProperty"]; + return map; +} + +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusPersonPlacesLivedItem +// + +@implementation GTLPlusPersonPlacesLivedItem +@dynamic primary, value; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusPersonUrlsItem +// + +@implementation GTLPlusPersonUrlsItem +@dynamic primary, type, value; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusPersonCoverCoverInfo +// + +@implementation GTLPlusPersonCoverCoverInfo +@dynamic leftImageOffset, topImageOffset; +@end + + +// ---------------------------------------------------------------------------- +// +// GTLPlusPersonCoverCoverPhoto +// + +@implementation GTLPlusPersonCoverCoverPhoto +@dynamic height, url, width; +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLQueryPlus.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLQueryPlus.h new file mode 100644 index 0000000000..44b43109c6 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLQueryPlus.h @@ -0,0 +1,297 @@ +/* Copyright (c) 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLQueryPlus.h +// + +// ---------------------------------------------------------------------------- +// NOTE: This file is generated from Google APIs Discovery Service. +// Service: +// Google+ API (plus/v1) +// Description: +// The Google+ API enables developers to build on top of the Google+ platform. +// Documentation: +// https://developers.google.com/+/api/ +// Classes: +// GTLQueryPlus (12 custom class methods, 15 custom properties) + +#if GTL_BUILT_AS_FRAMEWORK + #import "GTL/GTLQuery.h" +#else + #import "GTLQuery.h" +#endif + +@class GTLPlusMoment; + +@interface GTLQueryPlus : GTLQuery + +// +// Parameters valid on all methods. +// + +// Selector specifying which fields to include in a partial response. +@property (copy) NSString *fields; + +// +// Method-specific parameters; see the comments below for more information. +// +@property (copy) NSString *activityId; +@property (copy) NSString *collection; +@property (copy) NSString *commentId; +@property (assign) BOOL debug; +// identifier property maps to 'id' in JSON (to avoid Objective C's 'id'). +@property (copy) NSString *identifier; +@property (copy) NSString *language; +@property (assign) NSUInteger maxResults; +@property (copy) NSString *orderBy; +@property (copy) NSString *pageToken; +@property (copy) NSString *query; +@property (copy) NSString *sortOrder; +@property (copy) NSString *targetUrl; +@property (copy) NSString *type; +@property (copy) NSString *userId; + +#pragma mark - +#pragma mark "activities" methods +// These create a GTLQueryPlus object. + +// Method: plus.activities.get +// Get an activity. +// Required: +// activityId: The ID of the activity to get. +// Authorization scope(s): +// kGTLAuthScopePlusLogin +// kGTLAuthScopePlusMe +// Fetches a GTLPlusActivity. ++ (id)queryForActivitiesGetWithActivityId:(NSString *)activityId; + +// Method: plus.activities.list +// List all of the activities in the specified collection for a particular user. +// Required: +// userId: The ID of the user to get activities for. The special value "me" +// can be used to indicate the authenticated user. +// collection: The collection of activities to list. +// kGTLPlusCollectionPublic: All public activities created by the specified +// user. +// Optional: +// maxResults: The maximum number of activities to include in the response, +// which is used for paging. For any response, the actual number returned +// might be less than the specified maxResults. (1..100, default 20) +// pageToken: The continuation token, which is used to page through large +// result sets. To get the next page of results, set this parameter to the +// value of "nextPageToken" from the previous response. +// Authorization scope(s): +// kGTLAuthScopePlusLogin +// kGTLAuthScopePlusMe +// Fetches a GTLPlusActivityFeed. ++ (id)queryForActivitiesListWithUserId:(NSString *)userId + collection:(NSString *)collection; + +// Method: plus.activities.search +// Search public activities. +// Required: +// query: Full-text search query string. +// Optional: +// language: Specify the preferred language to search with. See search +// language codes for available values. (Default en-US) +// maxResults: The maximum number of activities to include in the response, +// which is used for paging. For any response, the actual number returned +// might be less than the specified maxResults. (1..20, default 10) +// orderBy: Specifies how to order search results. (Default +// kGTLPlusOrderByRecent) +// kGTLPlusOrderByBest: Sort activities by relevance to the user, most +// relevant first. +// kGTLPlusOrderByRecent: Sort activities by published date, most recent +// first. +// pageToken: The continuation token, which is used to page through large +// result sets. To get the next page of results, set this parameter to the +// value of "nextPageToken" from the previous response. This token can be of +// any length. +// Authorization scope(s): +// kGTLAuthScopePlusMe +// Fetches a GTLPlusActivityFeed. ++ (id)queryForActivitiesSearchWithQuery:(NSString *)query; + +#pragma mark - +#pragma mark "comments" methods +// These create a GTLQueryPlus object. + +// Method: plus.comments.get +// Get a comment. +// Required: +// commentId: The ID of the comment to get. +// Authorization scope(s): +// kGTLAuthScopePlusMe +// Fetches a GTLPlusComment. ++ (id)queryForCommentsGetWithCommentId:(NSString *)commentId; + +// Method: plus.comments.list +// List all of the comments for an activity. +// Required: +// activityId: The ID of the activity to get comments for. +// Optional: +// maxResults: The maximum number of comments to include in the response, +// which is used for paging. For any response, the actual number returned +// might be less than the specified maxResults. (0..500, default 20) +// pageToken: The continuation token, which is used to page through large +// result sets. To get the next page of results, set this parameter to the +// value of "nextPageToken" from the previous response. +// sortOrder: The order in which to sort the list of comments. (Default +// kGTLPlusSortOrderAscending) +// kGTLPlusSortOrderAscending: Sort oldest comments first. +// kGTLPlusSortOrderDescending: Sort newest comments first. +// Authorization scope(s): +// kGTLAuthScopePlusMe +// Fetches a GTLPlusCommentFeed. ++ (id)queryForCommentsListWithActivityId:(NSString *)activityId; + +#pragma mark - +#pragma mark "moments" methods +// These create a GTLQueryPlus object. + +// Method: plus.moments.insert +// Record a moment representing a user's activity such as making a purchase or +// commenting on a blog. +// Required: +// userId: The ID of the user to record activities for. The only valid values +// are "me" and the ID of the authenticated user. +// collection: The collection to which to write moments. +// kGTLPlusCollectionVault: The default collection for writing new moments. +// Optional: +// debug: Return the moment as written. Should be used only for debugging. +// Authorization scope(s): +// kGTLAuthScopePlusLogin +// Fetches a GTLPlusMoment. ++ (id)queryForMomentsInsertWithObject:(GTLPlusMoment *)object + userId:(NSString *)userId + collection:(NSString *)collection; + +// Method: plus.moments.list +// List all of the moments for a particular user. +// Required: +// userId: The ID of the user to get moments for. The special value "me" can +// be used to indicate the authenticated user. +// collection: The collection of moments to list. +// kGTLPlusCollectionVault: All moments created by the requesting +// application for the authenticated user. +// Optional: +// maxResults: The maximum number of moments to include in the response, which +// is used for paging. For any response, the actual number returned might be +// less than the specified maxResults. (1..100, default 20) +// pageToken: The continuation token, which is used to page through large +// result sets. To get the next page of results, set this parameter to the +// value of "nextPageToken" from the previous response. +// targetUrl: Only moments containing this targetUrl will be returned. +// type: Only moments of this type will be returned. +// Authorization scope(s): +// kGTLAuthScopePlusLogin +// Fetches a GTLPlusMomentsFeed. ++ (id)queryForMomentsListWithUserId:(NSString *)userId + collection:(NSString *)collection; + +// Method: plus.moments.remove +// Delete a moment. +// Required: +// identifier: The ID of the moment to delete. +// Authorization scope(s): +// kGTLAuthScopePlusLogin ++ (id)queryForMomentsRemoveWithIdentifier:(NSString *)identifier; + +#pragma mark - +#pragma mark "people" methods +// These create a GTLQueryPlus object. + +// Method: plus.people.get +// Get a person's profile. If your app uses scope +// https://www.googleapis.com/auth/plus.login, this method is guaranteed to +// return ageRange and language. +// Required: +// userId: The ID of the person to get the profile for. The special value "me" +// can be used to indicate the authenticated user. +// Authorization scope(s): +// kGTLAuthScopePlusLogin +// kGTLAuthScopePlusMe +// Fetches a GTLPlusPerson. ++ (id)queryForPeopleGetWithUserId:(NSString *)userId; + +// Method: plus.people.list +// List all of the people in the specified collection. +// Required: +// userId: Get the collection of people for the person identified by the ID or +// use "me" to indiciated the authenticated user. +// collection: The collection of people to list. +// kGTLPlusCollectionVisible: The list of people who this user has added to +// one or more circles, limited to the circles visible to the requesting +// application. +// Optional: +// maxResults: The maximum number of people to include in the response, which +// is used for paging. For any response, the actual number returned might be +// less than the specified maxResults. (1..100, default 100) +// orderBy: The order to return people in. +// kGTLPlusOrderByAlphabetical: Order the people by their display name. +// kGTLPlusOrderByBest: Order people based on the relevence to the viewer. +// pageToken: The continuation token, which is used to page through large +// result sets. To get the next page of results, set this parameter to the +// value of "nextPageToken" from the previous response. +// Authorization scope(s): +// kGTLAuthScopePlusLogin +// Fetches a GTLPlusPeopleFeed. ++ (id)queryForPeopleListWithUserId:(NSString *)userId + collection:(NSString *)collection; + +// Method: plus.people.listByActivity +// List all of the people in the specified collection for a particular activity. +// Required: +// activityId: The ID of the activity to get the list of people for. +// collection: The collection of people to list. +// kGTLPlusCollectionPlusoners: List all people who have +1'd this +// activity. +// kGTLPlusCollectionResharers: List all people who have reshared this +// activity. +// Optional: +// maxResults: The maximum number of people to include in the response, which +// is used for paging. For any response, the actual number returned might be +// less than the specified maxResults. (1..100, default 20) +// pageToken: The continuation token, which is used to page through large +// result sets. To get the next page of results, set this parameter to the +// value of "nextPageToken" from the previous response. +// Authorization scope(s): +// kGTLAuthScopePlusMe +// Fetches a GTLPlusPeopleFeed. ++ (id)queryForPeopleListByActivityWithActivityId:(NSString *)activityId + collection:(NSString *)collection; + +// Method: plus.people.search +// Search all public profiles. +// Required: +// query: Specify a query string for full text search of public text in all +// profiles. +// Optional: +// language: Specify the preferred language to search with. See search +// language codes for available values. (Default en-US) +// maxResults: The maximum number of people to include in the response, which +// is used for paging. For any response, the actual number returned might be +// less than the specified maxResults. (1..20, default 10) +// pageToken: The continuation token, which is used to page through large +// result sets. To get the next page of results, set this parameter to the +// value of "nextPageToken" from the previous response. This token can be of +// any length. +// Authorization scope(s): +// kGTLAuthScopePlusMe +// Fetches a GTLPlusPeopleFeed. ++ (id)queryForPeopleSearchWithQuery:(NSString *)query; + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLQueryPlus.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLQueryPlus.m new file mode 100644 index 0000000000..8d2c9a988f --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLQueryPlus.m @@ -0,0 +1,182 @@ +/* Copyright (c) 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLQueryPlus.m +// + +// ---------------------------------------------------------------------------- +// NOTE: This file is generated from Google APIs Discovery Service. +// Service: +// Google+ API (plus/v1) +// Description: +// The Google+ API enables developers to build on top of the Google+ platform. +// Documentation: +// https://developers.google.com/+/api/ +// Classes: +// GTLQueryPlus (12 custom class methods, 15 custom properties) + +#import "GTLQueryPlus.h" + +#import "GTLPlusActivity.h" +#import "GTLPlusActivityFeed.h" +#import "GTLPlusComment.h" +#import "GTLPlusCommentFeed.h" +#import "GTLPlusMoment.h" +#import "GTLPlusMomentsFeed.h" +#import "GTLPlusPeopleFeed.h" +#import "GTLPlusPerson.h" + +@implementation GTLQueryPlus + +@dynamic activityId, collection, commentId, debug, fields, identifier, language, + maxResults, orderBy, pageToken, query, sortOrder, targetUrl, type, + userId; + ++ (NSDictionary *)parameterNameMap { + NSDictionary *map = + [NSDictionary dictionaryWithObject:@"id" + forKey:@"identifier"]; + return map; +} + +#pragma mark - +#pragma mark "activities" methods +// These create a GTLQueryPlus object. + ++ (id)queryForActivitiesGetWithActivityId:(NSString *)activityId { + NSString *methodName = @"plus.activities.get"; + GTLQueryPlus *query = [self queryWithMethodName:methodName]; + query.activityId = activityId; + query.expectedObjectClass = [GTLPlusActivity class]; + return query; +} + ++ (id)queryForActivitiesListWithUserId:(NSString *)userId + collection:(NSString *)collection { + NSString *methodName = @"plus.activities.list"; + GTLQueryPlus *query = [self queryWithMethodName:methodName]; + query.userId = userId; + query.collection = collection; + query.expectedObjectClass = [GTLPlusActivityFeed class]; + return query; +} + ++ (id)queryForActivitiesSearchWithQuery:(NSString *)query_param { + NSString *methodName = @"plus.activities.search"; + GTLQueryPlus *query = [self queryWithMethodName:methodName]; + query.query = query_param; + query.expectedObjectClass = [GTLPlusActivityFeed class]; + return query; +} + +#pragma mark - +#pragma mark "comments" methods +// These create a GTLQueryPlus object. + ++ (id)queryForCommentsGetWithCommentId:(NSString *)commentId { + NSString *methodName = @"plus.comments.get"; + GTLQueryPlus *query = [self queryWithMethodName:methodName]; + query.commentId = commentId; + query.expectedObjectClass = [GTLPlusComment class]; + return query; +} + ++ (id)queryForCommentsListWithActivityId:(NSString *)activityId { + NSString *methodName = @"plus.comments.list"; + GTLQueryPlus *query = [self queryWithMethodName:methodName]; + query.activityId = activityId; + query.expectedObjectClass = [GTLPlusCommentFeed class]; + return query; +} + +#pragma mark - +#pragma mark "moments" methods +// These create a GTLQueryPlus object. + ++ (id)queryForMomentsInsertWithObject:(GTLPlusMoment *)object + userId:(NSString *)userId + collection:(NSString *)collection { + if (object == nil) { + GTL_DEBUG_ASSERT(object != nil, @"%@ got a nil object", NSStringFromSelector(_cmd)); + return nil; + } + NSString *methodName = @"plus.moments.insert"; + GTLQueryPlus *query = [self queryWithMethodName:methodName]; + query.bodyObject = object; + query.userId = userId; + query.collection = collection; + query.expectedObjectClass = [GTLPlusMoment class]; + return query; +} + ++ (id)queryForMomentsListWithUserId:(NSString *)userId + collection:(NSString *)collection { + NSString *methodName = @"plus.moments.list"; + GTLQueryPlus *query = [self queryWithMethodName:methodName]; + query.userId = userId; + query.collection = collection; + query.expectedObjectClass = [GTLPlusMomentsFeed class]; + return query; +} + ++ (id)queryForMomentsRemoveWithIdentifier:(NSString *)identifier { + NSString *methodName = @"plus.moments.remove"; + GTLQueryPlus *query = [self queryWithMethodName:methodName]; + query.identifier = identifier; + return query; +} + +#pragma mark - +#pragma mark "people" methods +// These create a GTLQueryPlus object. + ++ (id)queryForPeopleGetWithUserId:(NSString *)userId { + NSString *methodName = @"plus.people.get"; + GTLQueryPlus *query = [self queryWithMethodName:methodName]; + query.userId = userId; + query.expectedObjectClass = [GTLPlusPerson class]; + return query; +} + ++ (id)queryForPeopleListWithUserId:(NSString *)userId + collection:(NSString *)collection { + NSString *methodName = @"plus.people.list"; + GTLQueryPlus *query = [self queryWithMethodName:methodName]; + query.userId = userId; + query.collection = collection; + query.expectedObjectClass = [GTLPlusPeopleFeed class]; + return query; +} + ++ (id)queryForPeopleListByActivityWithActivityId:(NSString *)activityId + collection:(NSString *)collection { + NSString *methodName = @"plus.people.listByActivity"; + GTLQueryPlus *query = [self queryWithMethodName:methodName]; + query.activityId = activityId; + query.collection = collection; + query.expectedObjectClass = [GTLPlusPeopleFeed class]; + return query; +} + ++ (id)queryForPeopleSearchWithQuery:(NSString *)query_param { + NSString *methodName = @"plus.people.search"; + GTLQueryPlus *query = [self queryWithMethodName:methodName]; + query.query = query_param; + query.expectedObjectClass = [GTLPlusPeopleFeed class]; + return query; +} + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLServicePlus.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLServicePlus.h new file mode 100644 index 0000000000..b05c4f4707 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLServicePlus.h @@ -0,0 +1,61 @@ +/* Copyright (c) 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLServicePlus.h +// + +// ---------------------------------------------------------------------------- +// NOTE: This file is generated from Google APIs Discovery Service. +// Service: +// Google+ API (plus/v1) +// Description: +// The Google+ API enables developers to build on top of the Google+ platform. +// Documentation: +// https://developers.google.com/+/api/ +// Classes: +// GTLServicePlus (0 custom class methods, 0 custom properties) + +#if GTL_BUILT_AS_FRAMEWORK + #import "GTL/GTLService.h" +#else + #import "GTLService.h" +#endif + +@interface GTLServicePlus : GTLService + +// No new methods + +// Clients should create a standard query with any of the class methods in +// GTLQueryPlus.h. The query can the be sent with GTLService's execute methods, +// +// - (GTLServiceTicket *)executeQuery:(GTLQuery *)query +// completionHandler:(void (^)(GTLServiceTicket *ticket, +// id object, NSError *error))handler; +// or +// - (GTLServiceTicket *)executeQuery:(GTLQuery *)query +// delegate:(id)delegate +// didFinishSelector:(SEL)finishedSelector; +// +// where finishedSelector has a signature of: +// +// - (void)serviceTicket:(GTLServiceTicket *)ticket +// finishedWithObject:(id)object +// error:(NSError *)error; +// +// The object passed to the completion handler or delegate method +// is a subclass of GTLObject, determined by the query method executed. + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLServicePlus.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLServicePlus.m new file mode 100644 index 0000000000..6a53d566c3 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLPlus/GTLServicePlus.m @@ -0,0 +1,71 @@ +/* Copyright (c) 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLServicePlus.m +// + +// ---------------------------------------------------------------------------- +// NOTE: This file is generated from Google APIs Discovery Service. +// Service: +// Google+ API (plus/v1) +// Description: +// The Google+ API enables developers to build on top of the Google+ platform. +// Documentation: +// https://developers.google.com/+/api/ +// Classes: +// GTLServicePlus (0 custom class methods, 0 custom properties) + +#import "GTLPlus.h" + +@implementation GTLServicePlus + +#if DEBUG +// Method compiled in debug builds just to check that all the needed support +// classes are present at link time. ++ (NSArray *)checkClasses { + NSArray *classes = [NSArray arrayWithObjects: + [GTLQueryPlus class], + [GTLPlusAcl class], + [GTLPlusAclentryResource class], + [GTLPlusActivity class], + [GTLPlusActivityFeed class], + [GTLPlusComment class], + [GTLPlusCommentFeed class], + [GTLPlusItemScope class], + [GTLPlusMoment class], + [GTLPlusMomentsFeed class], + [GTLPlusPeopleFeed class], + [GTLPlusPerson class], + nil]; + return classes; +} +#endif // DEBUG + +- (id)init { + self = [super init]; + if (self) { + // Version from discovery. + self.apiVersion = @"v1"; + + // From discovery. Where to send JSON-RPC. + // Turn off prettyPrint for this service to save bandwidth (especially on + // mobile). The fetcher logging will pretty print. + self.rpcURL = [NSURL URLWithString:@"https://www.googleapis.com/rpc?prettyPrint=false"]; + } + return self; +} + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLQuery.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLQuery.h new file mode 100644 index 0000000000..39d0b108a0 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLQuery.h @@ -0,0 +1,135 @@ +/* Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLQuery.h +// + +// Query documentation: +// https://code.google.com/p/google-api-objectivec-client/wiki/Introduction#Query_Operations + +#import "GTLObject.h" +#import "GTLUploadParameters.h" + +@protocol GTLQueryProtocol +- (BOOL)isBatchQuery; +- (BOOL)shouldSkipAuthorization; +- (void)executionDidStop; +- (NSDictionary *)additionalHTTPHeaders; +- (GTLUploadParameters *)uploadParameters; +@end + +@protocol GTLQueryCollectionProtocol +@optional +@property (retain) NSString *pageToken; +@property (retain) NSNumber *startIndex; +@end + +@class GTLServiceTicket; + +@interface GTLQuery : NSObject { + @private + NSString *methodName_; + NSMutableDictionary *json_; + GTLObject *bodyObject_; + NSMutableDictionary *childCache_; + NSString *requestID_; + GTLUploadParameters *uploadParameters_; + NSDictionary *urlQueryParameters_; + NSDictionary *additionalHTTPHeaders_; + Class expectedObjectClass_; + BOOL skipAuthorization_; +#if NS_BLOCKS_AVAILABLE + void (^completionBlock_)(GTLServiceTicket *ticket, id object, NSError *error); +#elif !__LP64__ + // Placeholders: for 32-bit builds, keep the size of the object's ivar section + // the same with and without blocks + id completionPlaceholder_; +#endif +} + +// The rpc method name. +@property (readonly) NSString *methodName; + +// The JSON dictionary of all the parameters set on this query. +@property (retain) NSMutableDictionary *JSON; + +// The object set to be uploaded with the query. +@property (retain) GTLObject *bodyObject; + +// Each query must have a request ID string. The user may replace the +// default assigned request ID with a custom string, provided that if +// used in a batch query, all request IDs in the batch must be unique. +@property (copy) NSString *requestID; + +// For queries which support file upload, the MIME type and file handle +// or data must be provided. +@property (copy) GTLUploadParameters *uploadParameters; + +// Any url query parameters to add to the query (useful for debugging with some +// services). +@property (copy) NSDictionary *urlQueryParameters; + +// Any additional HTTP headers for this query. Not valid when this query +// is added to a batch. +// +// These headers override the same keys from the service object's +// additionalHTTPHeaders. +@property (copy) NSDictionary *additionalHTTPHeaders; + +// The GTLObject subclass expected for results (used if the result doesn't +// include a kind attribute). +@property (assign) Class expectedObjectClass; + +// Clients may set this to YES to disallow authorization. Defaults to NO. +@property (assign) BOOL shouldSkipAuthorization; + +#if NS_BLOCKS_AVAILABLE +// Clients may provide an optional callback block to be called immediately +// before the executeQuery: callback. +// +// The completionBlock property is particularly useful for queries executed +// in a batch. +// +// Errors passed to the completionBlock will have an "underlying" GTLErrorObject +// when the server returned an error for this specific query: +// +// GTLErrorObject *errorObj = [GTLErrorObject underlyingObjectForError:error]; +// if (errorObj) { +// // the server returned this error for this specific query +// } else { +// // the batch execution failed +// } +@property (copy) void (^completionBlock)(GTLServiceTicket *ticket, id object, NSError *error); +#endif + +// methodName is the RPC method name to use. ++ (id)queryWithMethodName:(NSString *)methodName GTL_NONNULL((1)); + +// methodName is the RPC method name to use. +- (id)initWithMethodName:(NSString *)method GTL_NONNULL((1)); + +// If you need to set a parameter that is not listed as a property for a +// query class, you can do so via this api. If you need to clear it after +// setting, pass nil for obj. +- (void)setCustomParameter:(id)obj forKey:(NSString *)key GTL_NONNULL((2)); + +// Auto-generated request IDs ++ (NSString *)nextRequestID; + +// Methods for subclasses to override. ++ (NSDictionary *)parameterNameMap; ++ (NSDictionary *)arrayPropertyToClassMap; +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLQuery.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLQuery.m new file mode 100644 index 0000000000..55f98223eb --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLQuery.m @@ -0,0 +1,267 @@ +/* Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLQuery.m +// + +#include + +#import "GTLQuery.h" +#import "GTLRuntimeCommon.h" +#import "GTLUtilities.h" + +@interface GTLQuery () +@end + +@implementation GTLQuery + +// Implementation Note: bodyObject could be done as a dynamic property and map +// it to the key "resource". But we expose the object on the ServiceTicket +// for developers, and so sending it through the plumbing already in the +// parameters and outside of that gets into a grey area. For requests sent +// via this class, we don't need to touch the JSON, but for developers that +// have to use the lower level apis for something we'd need to know to add +// it to the JSON. + +@synthesize methodName = methodName_, + JSON = json_, + bodyObject = bodyObject_, + requestID = requestID_, + uploadParameters = uploadParameters_, + urlQueryParameters = urlQueryParameters_, + additionalHTTPHeaders = additionalHTTPHeaders_, + expectedObjectClass = expectedObjectClass_, + shouldSkipAuthorization = skipAuthorization_; + +#if NS_BLOCKS_AVAILABLE +@synthesize completionBlock = completionBlock_; +#endif + ++ (id)queryWithMethodName:(NSString *)methodName { + return [[[self alloc] initWithMethodName:methodName] autorelease]; +} + +- (id)initWithMethodName:(NSString *)methodName { + self = [super init]; + if (self) { + requestID_ = [[[self class] nextRequestID] retain]; + + methodName_ = [methodName copy]; + if ([methodName_ length] == 0) { + [self release]; + self = nil; + } + } + return self; +} + +- (void)dealloc { + [methodName_ release]; + [json_ release]; + [bodyObject_ release]; + [childCache_ release]; + [requestID_ release]; + [uploadParameters_ release]; + [urlQueryParameters_ release]; + [additionalHTTPHeaders_ release]; +#if NS_BLOCKS_AVAILABLE + [completionBlock_ release]; +#endif + + [super dealloc]; +} + + +- (id)copyWithZone:(NSZone *)zone { + GTLQuery *query = + [[[self class] allocWithZone:zone] initWithMethodName:self.methodName]; + + if ([json_ count] > 0) { + // Deep copy the parameters + CFPropertyListRef ref = CFPropertyListCreateDeepCopy(kCFAllocatorDefault, + json_, kCFPropertyListMutableContainers); + query.JSON = [NSMakeCollectable(ref) autorelease]; + } + query.bodyObject = self.bodyObject; + query.requestID = self.requestID; + query.uploadParameters = self.uploadParameters; + query.urlQueryParameters = self.urlQueryParameters; + query.additionalHTTPHeaders = self.additionalHTTPHeaders; + query.expectedObjectClass = self.expectedObjectClass; + query.shouldSkipAuthorization = self.shouldSkipAuthorization; +#if NS_BLOCKS_AVAILABLE + query.completionBlock = self.completionBlock; +#endif + return query; +} + +- (NSString *)description { + NSArray *keys = [self.JSON allKeys]; + NSArray *params = [keys sortedArrayUsingSelector:@selector(compare:)]; + NSString *paramsSummary = @""; + if ([params count] > 0) { + paramsSummary = [NSString stringWithFormat:@" params:(%@)", + [params componentsJoinedByString:@","]]; + } + + keys = [self.urlQueryParameters allKeys]; + NSArray *urlQParams = [keys sortedArrayUsingSelector:@selector(compare:)]; + NSString *urlQParamsSummary = @""; + if ([urlQParams count] > 0) { + urlQParamsSummary = [NSString stringWithFormat:@" urlQParams:(%@)", + [urlQParams componentsJoinedByString:@","]]; + } + + GTLObject *bodyObj = self.bodyObject; + NSString *bodyObjSummary = @""; + if (bodyObj != nil) { + bodyObjSummary = [NSString stringWithFormat:@" bodyObject:%@", [bodyObj class]]; + } + + NSString *uploadStr = @""; + GTLUploadParameters *uploadParams = self.uploadParameters; + if (uploadParams) { + uploadStr = [NSString stringWithFormat:@" %@", uploadParams]; + } + + return [NSString stringWithFormat:@"%@ %p: {method:%@%@%@%@%@}", + [self class], self, self.methodName, + paramsSummary, urlQParamsSummary, bodyObjSummary, uploadStr]; +} + +- (void)setCustomParameter:(id)obj forKey:(NSString *)key { + [self setJSONValue:obj forKey:key]; +} + +- (BOOL)isBatchQuery { + return NO; +} + +- (void)executionDidStop { +#if NS_BLOCKS_AVAILABLE + self.completionBlock = nil; +#endif +} + ++ (NSString *)nextRequestID { + static unsigned long lastRequestID = 0; + NSString *result; + + @synchronized([GTLQuery class]) { + ++lastRequestID; + result = [NSString stringWithFormat:@"gtl_%lu", + (unsigned long) lastRequestID]; + } + return result; +} + +#pragma mark GTLRuntimeCommon Support + +- (void)setJSONValue:(id)obj forKey:(NSString *)key { + NSMutableDictionary *dict = self.JSON; + if (dict == nil && obj != nil) { + dict = [NSMutableDictionary dictionaryWithCapacity:1]; + self.JSON = dict; + } + [dict setValue:obj forKey:key]; +} + +- (id)JSONValueForKey:(NSString *)key { + id obj = [self.JSON objectForKey:key]; + return obj; +} + +// There is no property for childCache_ as there shouldn't be KVC/KVO +// support for it, it's an implementation detail. + +- (void)setCacheChild:(id)obj forKey:(NSString *)key { + if (childCache_ == nil && obj != nil) { + childCache_ = + [[NSMutableDictionary alloc] initWithObjectsAndKeys:obj, key, nil]; + } else { + [childCache_ setValue:obj forKey:key]; + } +} + +- (id)cacheChildForKey:(NSString *)key { + id obj = [childCache_ objectForKey:key]; + return obj; +} + +#pragma mark Methods for Subclasses to Override + ++ (NSDictionary *)parameterNameMap { + return nil; +} + ++ (NSDictionary *)arrayPropertyToClassMap { + return nil; +} + +#pragma mark Runtime Utilities + +static NSMutableDictionary *gParameterNameMapCache = nil; +static NSMutableDictionary *gArrayPropertyToClassMapCache = nil; + ++ (void)initialize { + // note that initialize is guaranteed by the runtime to be called in a + // thread-safe manner + if (gParameterNameMapCache == nil) { + gParameterNameMapCache = [GTLUtilities newStaticDictionary]; + } + if (gArrayPropertyToClassMapCache == nil) { + gArrayPropertyToClassMapCache = [GTLUtilities newStaticDictionary]; + } +} + ++ (NSDictionary *)propertyToJSONKeyMapForClass:(Class)aClass { + NSDictionary *resultMap = + [GTLUtilities mergedClassDictionaryForSelector:@selector(parameterNameMap) + startClass:aClass + ancestorClass:[GTLQuery class] + cache:gParameterNameMapCache]; + return resultMap; +} + ++ (NSDictionary *)arrayPropertyToClassMapForClass:(Class)aClass { + NSDictionary *resultMap = + [GTLUtilities mergedClassDictionaryForSelector:@selector(arrayPropertyToClassMap) + startClass:aClass + ancestorClass:[GTLQuery class] + cache:gArrayPropertyToClassMapCache]; + return resultMap; +} + +#pragma mark Runtime Support + +- (NSDictionary *)surrogates { + // Stub method just needed for RumtimeCommon, query doesn't use surrogates. + return nil; +} + ++ (Class)ancestorClass { + return [GTLQuery class]; +} + ++ (BOOL)resolveInstanceMethod:(SEL)sel { + BOOL resolved = [GTLRuntimeCommon resolveInstanceMethod:sel onClass:self]; + if (resolved) + return YES; + + return [super resolveInstanceMethod:sel]; +} + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLRuntimeCommon.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLRuntimeCommon.h new file mode 100644 index 0000000000..28822b5ed9 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLRuntimeCommon.h @@ -0,0 +1,57 @@ +/* Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLRuntimeCommon.h +// + + +#import + +#import "GTLDefines.h" + +// This protocol and support class are an internal implementation detail so +// GTLObject and GTLQuery can share some code. + +@protocol GTLRuntimeCommon +@required +// Get/Set properties +- (void)setJSONValue:(id)obj forKey:(NSString *)key; +- (id)JSONValueForKey:(NSString *)key; +// Child cache +- (void)setCacheChild:(id)obj forKey:(NSString *)key; +- (id)cacheChildForKey:(NSString *)key; +// Surrogate class mappings. +- (NSDictionary *)surrogates; +// Key map ++ (NSDictionary *)propertyToJSONKeyMapForClass:(Class)aClass; +// Array item types ++ (NSDictionary *)arrayPropertyToClassMapForClass:(Class)aClass; +// The parent class for dynamic support ++ (Class)ancestorClass; +@end + +@interface GTLRuntimeCommon : NSObject +// Wire things up. ++ (BOOL)resolveInstanceMethod:(SEL)sel onClass:(Class)onClass; +// Helpers ++ (id)objectFromJSON:(id)json + defaultClass:(Class)defaultClass + surrogates:(NSDictionary *)surrogates + isCacheable:(BOOL*)isCacheable; ++ (id)jsonFromAPIObject:(id)obj + expectedClass:(Class)expectedClass + isCacheable:(BOOL*)isCacheable; +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLRuntimeCommon.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLRuntimeCommon.m new file mode 100644 index 0000000000..5f6613d036 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLRuntimeCommon.m @@ -0,0 +1,1141 @@ +/* Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLRuntimeCommon.m +// + +#include + +#import "GTLRuntimeCommon.h" + +#import "GTLDateTime.h" +#import "GTLObject.h" +#import "GTLUtilities.h" + +static NSString *const kReturnClassKey = @"returnClass"; +static NSString *const kContainedClassKey = @"containedClass"; +static NSString *const kJSONKey = @"jsonKey"; + +// Note: NSObject's class is used as a marker for the expected/default class +// when Discovery says it can be any type of object. + +@implementation GTLRuntimeCommon + +// Helper to generically convert JSON to an api object type. ++ (id)objectFromJSON:(id)json + defaultClass:(Class)defaultClass + surrogates:(NSDictionary *)surrogates + isCacheable:(BOOL*)isCacheable { + id result = nil; + BOOL canBeCached = YES; + + // TODO(TVL): use defaultClass to validate things like expectedClass is + // done in jsonFromAPIObject:expectedClass:isCacheable:? + + if ([json isKindOfClass:[NSDictionary class]]) { + // If no default, or the default was any object, then default to base + // object here (and hope there is a kind to get the right thing). + if ((defaultClass == Nil) || [defaultClass isEqual:[NSObject class]]) { + defaultClass = [GTLObject class]; + } + result = [GTLObject objectForJSON:json + defaultClass:defaultClass + surrogates:surrogates + batchClassMap:nil]; + } else if ([json isKindOfClass:[NSArray class]]) { + NSArray *jsonArray = json; + // make an object for each JSON dictionary in the array + NSMutableArray *resultArray = [NSMutableArray arrayWithCapacity:[jsonArray count]]; + for (id jsonItem in jsonArray) { + id item = [self objectFromJSON:jsonItem + defaultClass:defaultClass + surrogates:surrogates + isCacheable:NULL]; + [resultArray addObject:item]; + } + result = resultArray; + } else if ([json isKindOfClass:[NSString class]]) { + // DateTimes live in JSON as strings, so convert + if ([defaultClass isEqual:[GTLDateTime class]]) { + result = [GTLDateTime dateTimeWithRFC3339String:json]; + } else { + result = json; + canBeCached = NO; + } + } else if ([json isKindOfClass:[NSNumber class]] || + [json isKindOfClass:[NSNull class]]) { + result = json; + canBeCached = NO; + } else { + GTL_DEBUG_LOG(@"GTLRuntimeCommon: unsupported class '%s' in objectFromJSON", + class_getName([json class])); + } + + if (isCacheable) { + *isCacheable = canBeCached; + } + return result; +} + +// Helper to generically convert an api object type to JSON. +// |expectedClass| is the type that was expected for |obj|. ++ (id)jsonFromAPIObject:(id)obj + expectedClass:(Class)expectedClass + isCacheable:(BOOL*)isCacheable { + id result = nil; + BOOL canBeCached = YES; + BOOL checkExpected = (expectedClass != Nil); + + if ([obj isKindOfClass:[NSString class]]) { + result = [[obj copy] autorelease]; + canBeCached = NO; + } else if ([obj isKindOfClass:[NSNumber class]] || + [obj isKindOfClass:[NSNull class]]) { + result = obj; + canBeCached = NO; + } else if ([obj isKindOfClass:[GTLObject class]]) { + result = [obj JSON]; + if (result == nil) { + // adding an empty object; it should have a JSON dictionary so it can + // hold future assignments + [obj setJSON:[NSMutableDictionary dictionary]]; + result = [obj JSON]; + } + } else if ([obj isKindOfClass:[NSArray class]]) { + checkExpected = NO; + NSArray *array = obj; + // get the JSON for each thing in the array + NSMutableArray *resultArray = [NSMutableArray arrayWithCapacity:[array count]]; + for (id item in array) { + id itemJSON = [self jsonFromAPIObject:item + expectedClass:expectedClass + isCacheable:NULL]; + [resultArray addObject:itemJSON]; + } + result = resultArray; + } else if ([obj isKindOfClass:[GTLDateTime class]]) { + // DateTimes live in JSON as strings, so convert. + GTLDateTime *dateTime = obj; + result = [dateTime stringValue]; + } else { + checkExpected = NO; + GTL_DEBUG_LOG(@"GTLRuntimeCommon: unsupported class '%s' in jsonFromAPIObject", + class_getName([obj class])); + } + + if (checkExpected) { + // If the default was any object, then clear it to skip validation checks. + if ([expectedClass isEqual:[NSObject class]] || + [obj isKindOfClass:[NSNull class]]) { + expectedClass = nil; + } + if (expectedClass && ![obj isKindOfClass:expectedClass]) { + GTL_DEBUG_LOG(@"GTLRuntimeCommon: jsonFromAPIObject expected class '%s' instead got '%s'", + class_getName(expectedClass), class_getName([obj class])); + } + } + + if (isCacheable) { + *isCacheable = canBeCached; + } + return result; +} + +#pragma mark JSON/Object Utilities + +static NSMutableDictionary *gDispatchCache = nil; + +static CFStringRef SelectorKeyCopyDescriptionCallBack(const void *key) { + // Make a CFString from the key + NSString *name = NSStringFromSelector((SEL) key); + CFStringRef str = CFStringCreateCopy(kCFAllocatorDefault, (CFStringRef) name); + return str; +} + +// Save the dispatch details for the specified class and selector ++ (void)setStoredDispatchForClass:(Class)dispatchClass + selector:(SEL)sel + returnClass:(Class)returnClass + containedClass:(Class)containedClass + jsonKey:(NSString *)jsonKey { + // cache structure: + // class -> + // selector -> + // returnClass + // containedClass + // jsonKey + @synchronized([GTLRuntimeCommon class]) { + if (gDispatchCache == nil) { + gDispatchCache = [GTLUtilities newStaticDictionary]; + } + + CFMutableDictionaryRef classDict = + (CFMutableDictionaryRef) [gDispatchCache objectForKey:dispatchClass]; + if (classDict == nil) { + // We create a CFDictionary since the keys are raw selectors rather than + // NSStrings + const CFDictionaryKeyCallBacks keyCallBacks = { + .version = 0, + .retain = NULL, + .release = NULL, + .copyDescription = SelectorKeyCopyDescriptionCallBack, + .equal = NULL, // defaults to pointer comparison + .hash = NULL // defaults to the pointer value + }; + const CFIndex capacity = 0; // no limit + classDict = CFDictionaryCreateMutable(kCFAllocatorDefault, capacity, + &keyCallBacks, + &kCFTypeDictionaryValueCallBacks); + [gDispatchCache setObject:(id)classDict + forKey:(id)dispatchClass]; + CFRelease(classDict); + } + + NSDictionary *selDict = (NSDictionary *)CFDictionaryGetValue(classDict, sel); + if (selDict == nil) { + selDict = [NSDictionary dictionaryWithObjectsAndKeys: + jsonKey, kJSONKey, + returnClass, kReturnClassKey, // can be nil (primitive types) + containedClass, kContainedClassKey, // may be nil + nil]; + CFDictionarySetValue(classDict, sel, selDict); + } else { + // we already have a dictionary for this selector on this class, which is + // surprising + GTL_DEBUG_LOG(@"Storing duplicate dispatch for %@ selector %@", + dispatchClass, NSStringFromSelector(sel)); + } + } +} + ++ (BOOL)getStoredDispatchForClass:(Class)dispatchClass + selector:(SEL)sel + returnClass:(Class *)outReturnClass + containedClass:(Class *)outContainedClass + jsonKey:(NSString **)outJsonKey { + @synchronized([GTLRuntimeCommon class]) { + // walk from this class up the hierarchy to the ancestor class + Class topClass = class_getSuperclass([dispatchClass ancestorClass]); + for (Class currClass = dispatchClass; + currClass != topClass; + currClass = class_getSuperclass(currClass)) { + + CFMutableDictionaryRef classDict = + (CFMutableDictionaryRef) [gDispatchCache objectForKey:currClass]; + if (classDict) { + NSMutableDictionary *selDict = + (NSMutableDictionary *) CFDictionaryGetValue(classDict, sel); + if (selDict) { + if (outReturnClass) { + *outReturnClass = [selDict objectForKey:kReturnClassKey]; + } + if (outContainedClass) { + *outContainedClass = [selDict objectForKey:kContainedClassKey]; + } + if (outJsonKey) { + *outJsonKey = [selDict objectForKey:kJSONKey]; + } + return YES; + } + } + } + } + GTL_DEBUG_LOG(@"Failed to find stored dispatch info for %@ %s", + dispatchClass, sel_getName(sel)); + return NO; +} + +#pragma mark IMPs - getters and setters for specific object types + +#if !__LP64__ + +// NSInteger on 32bit +static NSInteger DynamicInteger32Getter(id self, SEL sel) { + // get an NSInteger (NSNumber) from the JSON dictionary + NSString *jsonKey = nil; + Class selfClass = [self class]; + if ([GTLRuntimeCommon getStoredDispatchForClass:selfClass + selector:sel + returnClass:NULL + containedClass:NULL + jsonKey:&jsonKey]) { + NSNumber *num = [self JSONValueForKey:jsonKey]; + num = GTL_EnsureNSNumber(num); + NSInteger result = [num integerValue]; + return result; + } + return 0; +} + +static void DynamicInteger32Setter(id self, SEL sel, NSInteger val) { + // save an NSInteger (NSNumber) into the JSON dictionary + NSString *jsonKey = nil; + Class selfClass = [self class]; + if ([GTLRuntimeCommon getStoredDispatchForClass:selfClass + selector:sel + returnClass:NULL + containedClass:NULL + jsonKey:&jsonKey]) { + NSNumber *num = [NSNumber numberWithInteger:val]; + [self setJSONValue:num forKey:jsonKey]; + } +} + +// NSUInteger on 32bit +static NSUInteger DynamicUInteger32Getter(id self, SEL sel) { + // get an NSUInteger (NSNumber) from the JSON dictionary + NSString *jsonKey = nil; + Class selfClass = [self class]; + if ([GTLRuntimeCommon getStoredDispatchForClass:selfClass + selector:sel + returnClass:NULL + containedClass:NULL + jsonKey:&jsonKey]) { + NSNumber *num = [self JSONValueForKey:jsonKey]; + num = GTL_EnsureNSNumber(num); + NSUInteger result = [num unsignedIntegerValue]; + return result; + } + return 0; +} + +static void DynamicUInteger32Setter(id self, SEL sel, NSUInteger val) { + // save an NSUInteger (NSNumber) into the JSON dictionary + NSString *jsonKey = nil; + Class selfClass = [self class]; + if ([GTLRuntimeCommon getStoredDispatchForClass:selfClass + selector:sel + returnClass:NULL + containedClass:NULL + jsonKey:&jsonKey]) { + NSNumber *num = [NSNumber numberWithUnsignedInteger:val]; + [self setJSONValue:num forKey:jsonKey]; + } +} + +#endif // !__LP64__ + +// NSInteger on 64bit, long long on 32bit and 64bit +static long long DynamicLongLongGetter(id self, SEL sel) { + // get a long long (NSNumber) from the JSON dictionary + NSString *jsonKey = nil; + Class selfClass = [self class]; + if ([GTLRuntimeCommon getStoredDispatchForClass:selfClass + selector:sel + returnClass:NULL + containedClass:NULL + jsonKey:&jsonKey]) { + NSNumber *num = [self JSONValueForKey:jsonKey]; + num = GTL_EnsureNSNumber(num); + long long result = [num longLongValue]; + return result; + } + return 0; +} + +static void DynamicLongLongSetter(id self, SEL sel, long long val) { + // save a long long (NSNumber) into the JSON dictionary + NSString *jsonKey = nil; + Class selfClass = [self class]; + if ([GTLRuntimeCommon getStoredDispatchForClass:selfClass + selector:sel + returnClass:NULL + containedClass:NULL + jsonKey:&jsonKey]) { + NSNumber *num = [NSNumber numberWithLongLong:val]; + [self setJSONValue:num forKey:jsonKey]; + } +} + +// NSUInteger on 64bit, unsiged long long on 32bit and 64bit +static unsigned long long DynamicULongLongGetter(id self, SEL sel) { + // get an unsigned long long (NSNumber) from the JSON dictionary + NSString *jsonKey = nil; + Class selfClass = [self class]; + if ([GTLRuntimeCommon getStoredDispatchForClass:selfClass + selector:sel + returnClass:NULL + containedClass:NULL + jsonKey:&jsonKey]) { + NSNumber *num = [self JSONValueForKey:jsonKey]; + num = GTL_EnsureNSNumber(num); + unsigned long long result = [num unsignedLongLongValue]; + return result; + } + return 0; +} + +static void DynamicULongLongSetter(id self, SEL sel, unsigned long long val) { + // save an unsigned long long (NSNumber) into the JSON dictionary + NSString *jsonKey = nil; + Class selfClass = [self class]; + if ([GTLRuntimeCommon getStoredDispatchForClass:selfClass + selector:sel + returnClass:NULL + containedClass:NULL + jsonKey:&jsonKey]) { + NSNumber *num = [NSNumber numberWithUnsignedLongLong:val]; + [self setJSONValue:num forKey:jsonKey]; + } +} + +// float +static float DynamicFloatGetter(id self, SEL sel) { + // get a float (NSNumber) from the JSON dictionary + NSString *jsonKey = nil; + Class selfClass = [self class]; + if ([GTLRuntimeCommon getStoredDispatchForClass:selfClass + selector:sel + returnClass:NULL + containedClass:NULL + jsonKey:&jsonKey]) { + NSNumber *num = [self JSONValueForKey:jsonKey]; + num = GTL_EnsureNSNumber(num); + float result = [num floatValue]; + return result; + } + return 0.0f; +} + +static void DynamicFloatSetter(id self, SEL sel, float val) { + // save a float (NSNumber) into the JSON dictionary + NSString *jsonKey = nil; + Class selfClass = [self class]; + if ([GTLRuntimeCommon getStoredDispatchForClass:selfClass + selector:sel + returnClass:NULL + containedClass:NULL + jsonKey:&jsonKey]) { + NSNumber *num = [NSNumber numberWithFloat:val]; + [self setJSONValue:num forKey:jsonKey]; + } +} + +// double +static double DynamicDoubleGetter(id self, SEL sel) { + // get a double (NSNumber) from the JSON dictionary + NSString *jsonKey = nil; + Class selfClass = [self class]; + if ([GTLRuntimeCommon getStoredDispatchForClass:selfClass + selector:sel + returnClass:NULL + containedClass:NULL + jsonKey:&jsonKey]) { + NSNumber *num = [self JSONValueForKey:jsonKey]; + num = GTL_EnsureNSNumber(num); + double result = [num doubleValue]; + return result; + } + return 0.0; +} + +static void DynamicDoubleSetter(id self, SEL sel, double val) { + // save a double (NSNumber) into the JSON dictionary + NSString *jsonKey = nil; + Class selfClass = [self class]; + if ([GTLRuntimeCommon getStoredDispatchForClass:selfClass + selector:sel + returnClass:NULL + containedClass:NULL + jsonKey:&jsonKey]) { + NSNumber *num = [NSNumber numberWithDouble:val]; + [self setJSONValue:num forKey:jsonKey]; + } +} + +// BOOL +static BOOL DynamicBooleanGetter(id self, SEL sel) { + // get a BOOL (NSNumber) from the JSON dictionary + NSString *jsonKey = nil; + Class selfClass = [self class]; + if ([GTLRuntimeCommon getStoredDispatchForClass:selfClass + selector:sel + returnClass:NULL + containedClass:NULL + jsonKey:&jsonKey]) { + NSNumber *num = [self JSONValueForKey:jsonKey]; + BOOL flag = [num boolValue]; + return flag; + } + return NO; +} + +static void DynamicBooleanSetter(id self, SEL sel, BOOL val) { + // save a BOOL (NSNumber) into the JSON dictionary + NSString *jsonKey = nil; + Class selfClass = [self class]; + if ([GTLRuntimeCommon getStoredDispatchForClass:selfClass + selector:sel + returnClass:NULL + containedClass:NULL + jsonKey:&jsonKey]) { + NSNumber *num = [NSNumber numberWithBool:val]; + [self setJSONValue:num forKey:jsonKey]; + } +} + +// NSString +static NSString *DynamicStringGetter(id self, SEL sel) { + // get an NSString from the JSON dictionary + NSString *jsonKey = nil; + Class selfClass = [self class]; + if ([GTLRuntimeCommon getStoredDispatchForClass:selfClass + selector:sel + returnClass:NULL + containedClass:NULL + jsonKey:&jsonKey]) { + + NSString *str = [self JSONValueForKey:jsonKey]; + return str; + } + return nil; +} + +static void DynamicStringSetter(id self, SEL sel, + NSString *str) { + // save an NSString into the JSON dictionary + NSString *jsonKey = nil; + Class selfClass = [self class]; + if ([GTLRuntimeCommon getStoredDispatchForClass:selfClass + selector:sel + returnClass:NULL + containedClass:NULL + jsonKey:&jsonKey]) { + NSString *copiedStr = [str copy]; + [self setJSONValue:copiedStr forKey:jsonKey]; + [copiedStr release]; + } +} + +// GTLDateTime +static GTLDateTime *DynamicDateTimeGetter(id self, SEL sel) { + // get a GTLDateTime from the JSON dictionary + NSString *jsonKey = nil; + Class selfClass = [self class]; + if ([GTLRuntimeCommon getStoredDispatchForClass:selfClass + selector:sel + returnClass:NULL + containedClass:NULL + jsonKey:&jsonKey]) { + + // Return the cached object before creating on demand. + GTLDateTime *cachedDateTime = [self cacheChildForKey:jsonKey]; + if (cachedDateTime != nil) { + return cachedDateTime; + } + NSString *str = [self JSONValueForKey:jsonKey]; + id cacheValue, resultValue; + if (![str isKindOfClass:[NSNull class]]) { + GTLDateTime *dateTime = [GTLDateTime dateTimeWithRFC3339String:str]; + + cacheValue = dateTime; + resultValue = dateTime; + } else { + cacheValue = nil; + resultValue = [NSNull null]; + } + [self setCacheChild:cacheValue forKey:jsonKey]; + return resultValue; + } + return nil; +} + +static void DynamicDateTimeSetter(id self, SEL sel, + GTLDateTime *dateTime) { + // save an GTLDateTime into the JSON dictionary + NSString *jsonKey = nil; + Class selfClass = [self class]; + if ([GTLRuntimeCommon getStoredDispatchForClass:selfClass + selector:sel + returnClass:NULL + containedClass:NULL + jsonKey:&jsonKey]) { + id cacheValue, jsonValue; + if (![dateTime isKindOfClass:[NSNull class]]) { + jsonValue = [dateTime stringValue]; + cacheValue = dateTime; + } else { + jsonValue = [NSNull null]; + cacheValue = nil; + } + + [self setJSONValue:jsonValue forKey:jsonKey]; + [self setCacheChild:cacheValue forKey:jsonKey]; + } +} + +// NSNumber +static NSNumber *DynamicNumberGetter(id self, SEL sel) { + // get an NSNumber from the JSON dictionary + NSString *jsonKey = nil; + Class selfClass = [self class]; + if ([GTLRuntimeCommon getStoredDispatchForClass:selfClass + selector:sel + returnClass:NULL + containedClass:NULL + jsonKey:&jsonKey]) { + + NSNumber *num = [self JSONValueForKey:jsonKey]; + num = GTL_EnsureNSNumber(num); + return num; + } + return nil; +} + +static void DynamicNumberSetter(id self, SEL sel, + NSNumber *num) { + // save an NSNumber into the JSON dictionary + NSString *jsonKey = nil; + Class selfClass = [self class]; + if ([GTLRuntimeCommon getStoredDispatchForClass:selfClass + selector:sel + returnClass:NULL + containedClass:NULL + jsonKey:&jsonKey]) { + [self setJSONValue:num forKey:jsonKey]; + } +} + +// GTLObject +static GTLObject *DynamicObjectGetter(id self, SEL sel) { + // get a GTLObject from the JSON dictionary + NSString *jsonKey = nil; + Class returnClass = nil; + Class selfClass = [self class]; + if ([GTLRuntimeCommon getStoredDispatchForClass:selfClass + selector:sel + returnClass:&returnClass + containedClass:NULL + jsonKey:&jsonKey]) { + + // Return the cached object before creating on demand. + GTLObject *cachedObj = [self cacheChildForKey:jsonKey]; + if (cachedObj != nil) { + return cachedObj; + } + NSMutableDictionary *dict = [self JSONValueForKey:jsonKey]; + if ([dict isKindOfClass:[NSMutableDictionary class]]) { + // get the class of the object being returned, and instantiate it + if (returnClass == Nil) { + returnClass = [GTLObject class]; + } + + NSDictionary *surrogates = self.surrogates; + GTLObject *obj = [GTLObject objectForJSON:dict + defaultClass:returnClass + surrogates:surrogates + batchClassMap:nil]; + [self setCacheChild:obj forKey:jsonKey]; + return obj; + } else if ([dict isKindOfClass:[NSNull class]]) { + [self setCacheChild:nil forKey:jsonKey]; + return (id) [NSNull null]; + } else if (dict != nil) { + // unexpected; probably got a string -- let the caller figure it out + GTL_DEBUG_LOG(@"GTLObject: unexpected JSON: %@.%@ should be a dictionary, actually is a %@:\n%@", + NSStringFromClass(selfClass), NSStringFromSelector(sel), + NSStringFromClass([dict class]), dict); + return (GTLObject *)dict; + } + } + return nil; +} + +static void DynamicObjectSetter(id self, SEL sel, + GTLObject *obj) { + // save a GTLObject into the JSON dictionary + NSString *jsonKey = nil; + Class selfClass = [self class]; + if ([GTLRuntimeCommon getStoredDispatchForClass:selfClass + selector:sel + returnClass:NULL + containedClass:NULL + jsonKey:&jsonKey]) { + id cacheValue, jsonValue; + if (![obj isKindOfClass:[NSNull class]]) { + NSMutableDictionary *dict = [obj JSON]; + if (dict == nil && obj != nil) { + // adding an empty object; it should have a JSON dictionary so it can + // hold future assignments + obj.JSON = [NSMutableDictionary dictionary]; + jsonValue = obj.JSON; + } else { + jsonValue = dict; + } + cacheValue = obj; + } else { + jsonValue = [NSNull null]; + cacheValue = nil; + } + [self setJSONValue:jsonValue forKey:jsonKey]; + [self setCacheChild:cacheValue forKey:jsonKey]; + } +} + +// get an NSArray of GTLObjects, NSStrings, or NSNumbers from the +// JSON dictionary for this object +static NSMutableArray *DynamicArrayGetter(id self, SEL sel) { + NSString *jsonKey = nil; + Class containedClass = nil; + Class selfClass = [self class]; + if ([GTLRuntimeCommon getStoredDispatchForClass:selfClass + selector:sel + returnClass:NULL + containedClass:&containedClass + jsonKey:&jsonKey]) { + + // Return the cached array before creating on demand. + NSMutableArray *cachedArray = [self cacheChildForKey:jsonKey]; + if (cachedArray != nil) { + return cachedArray; + } + NSMutableArray *result = nil; + NSArray *array = [self JSONValueForKey:jsonKey]; + if (array != nil) { + if ([array isKindOfClass:[NSArray class]]) { + NSDictionary *surrogates = self.surrogates; + result = [GTLRuntimeCommon objectFromJSON:array + defaultClass:containedClass + surrogates:surrogates + isCacheable:NULL]; + } else { +#if DEBUG + if (![array isKindOfClass:[NSNull class]]) { + GTL_DEBUG_LOG(@"GTLObject: unexpected JSON: %@.%@ should be an array, actually is a %@:\n%@", + NSStringFromClass(selfClass), NSStringFromSelector(sel), + NSStringFromClass([array class]), array); + } +#endif + result = (NSMutableArray *)array; + } + } + + [self setCacheChild:result forKey:jsonKey]; + return result; + } + return nil; +} + +static void DynamicArraySetter(id self, SEL sel, + NSMutableArray *array) { + // save an array of GTLObjects objects into the JSON dictionary + NSString *jsonKey = nil; + Class selfClass = [self class]; + Class containedClass = nil; + if ([GTLRuntimeCommon getStoredDispatchForClass:selfClass + selector:sel + returnClass:NULL + containedClass:&containedClass + jsonKey:&jsonKey]) { + id json = [GTLRuntimeCommon jsonFromAPIObject:array + expectedClass:containedClass + isCacheable:NULL]; + [self setJSONValue:json forKey:jsonKey]; + [self setCacheChild:array forKey:jsonKey]; + } +} + +// type 'id' +static id DynamicNSObjectGetter(id self, SEL sel) { + NSString *jsonKey = nil; + Class returnClass = nil; + Class selfClass = [self class]; + if ([GTLRuntimeCommon getStoredDispatchForClass:selfClass + selector:sel + returnClass:&returnClass + containedClass:NULL + jsonKey:&jsonKey]) { + + // Return the cached object before creating on demand. + id cachedObj = [self cacheChildForKey:jsonKey]; + if (cachedObj != nil) { + return cachedObj; + } + + id jsonObj = [self JSONValueForKey:jsonKey]; + if (jsonObj != nil) { + BOOL shouldCache = NO; + NSDictionary *surrogates = self.surrogates; + id result = [GTLRuntimeCommon objectFromJSON:jsonObj + defaultClass:nil + surrogates:surrogates + isCacheable:&shouldCache]; + + [self setCacheChild:(shouldCache ? result : nil) + forKey:jsonKey]; + return result; + } + } + return nil; +} + +static void DynamicNSObjectSetter(id self, SEL sel, id obj) { + NSString *jsonKey = nil; + Class selfClass = [self class]; + if ([GTLRuntimeCommon getStoredDispatchForClass:selfClass + selector:sel + returnClass:NULL + containedClass:NULL + jsonKey:&jsonKey]) { + BOOL shouldCache = NO; + id json = [GTLRuntimeCommon jsonFromAPIObject:obj + expectedClass:Nil + isCacheable:&shouldCache]; + [self setJSONValue:json forKey:jsonKey]; + [self setCacheChild:(shouldCache ? obj : nil) + forKey:jsonKey]; + } +} + +#pragma mark Runtime lookup support + +static objc_property_t PropertyForSel(Class startClass, + SEL sel, BOOL isSetter, + Class *outFoundClass) { + const char *baseName = sel_getName(sel); + size_t baseNameLen = strlen(baseName); + if (isSetter) { + baseName += 3; // skip "set" + baseNameLen -= 4; // subtract "set" and the final colon + } + + // walk from this class up the hierarchy to the ancestor class + Class topClass = class_getSuperclass([startClass ancestorClass]); + for (Class currClass = startClass; + currClass != topClass; + currClass = class_getSuperclass(currClass)) { + // step through this class's properties + objc_property_t foundProp = NULL; + objc_property_t *properties = class_copyPropertyList(currClass, NULL); + if (properties) { + for (objc_property_t *prop = properties; *prop != NULL; ++prop) { + const char *propName = property_getName(*prop); + size_t propNameLen = strlen(propName); + + // search for an exact-name match (a getter), but case-insensitive on the + // first character (in case baseName comes from a setter) + if (baseNameLen == propNameLen + && strncasecmp(baseName, propName, 1) == 0 + && (baseNameLen <= 1 + || strncmp(baseName + 1, propName + 1, baseNameLen - 1) == 0)) { + // return the actual property name + foundProp = *prop; + + // if requested, return the class containing the property + if (outFoundClass) *outFoundClass = currClass; + break; + } + } + free(properties); + } + if (foundProp) return foundProp; + } + + // not found; this occasionally happens when the system looks for a method + // like "getFoo" or "descriptionWithLocale:indent:" + return NULL; +} + +typedef struct { + const char *attributePrefix; + + const char *setterEncoding; + IMP setterFunction; + const char *getterEncoding; + IMP getterFunction; + + // These are the "fixed" return classes, but some properties will require + // looking up the return class instead (because it is a subclass of + // GTLObject). + const char *returnClassName; + Class returnClass; + BOOL extractReturnClass; + +} GTLDynamicImpInfo; + +static const GTLDynamicImpInfo *DynamicImpInfoForProperty(objc_property_t prop, + Class *outReturnClass) { + + if (outReturnClass) *outReturnClass = nil; + + // dynamic method resolution: + // http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtDynamicResolution.html + // + // property runtimes: + // http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html + + // Get and parse the property attributes, which look something like + // T@"NSString",&,D,P + // Ti,D -- NSInteger on 32bit + // Tq,D -- NSInteger on 64bit, long long on 32bit & 64bit + // Tc,D -- BOOL comes as char + // T@"NSString",D + // T@"GTLLink",D + // T@"NSArray",D + + + static GTLDynamicImpInfo kImplInfo[] = { +#if !__LP64__ + { // NSInteger on 32bit + "Ti", + "v@:i", (IMP)DynamicInteger32Setter, + "i@:", (IMP)DynamicInteger32Getter, + nil, nil, + NO + }, + { // NSUInteger on 32bit + "TI", + "v@:I", (IMP)DynamicUInteger32Setter, + "I@:", (IMP)DynamicUInteger32Getter, + nil, nil, + NO + }, +#endif + { // NSInteger on 64bit, long long on 32bit and 64bit. + "Tq", + "v@:q", (IMP)DynamicLongLongSetter, + "q@:", (IMP)DynamicLongLongGetter, + nil, nil, + NO + }, + { // NSUInteger on 64bit, long long on 32bit and 64bit. + "TQ", + "v@:Q", (IMP)DynamicULongLongSetter, + "Q@:", (IMP)DynamicULongLongGetter, + nil, nil, + NO + }, + { // float + "Tf", + "v@:f", (IMP)DynamicFloatSetter, + "f@:", (IMP)DynamicFloatGetter, + nil, nil, + NO + }, + { // double + "Td", + "v@:d", (IMP)DynamicDoubleSetter, + "d@:", (IMP)DynamicDoubleGetter, + nil, nil, + NO + }, + { // BOOL + "Tc", + "v@:c", (IMP)DynamicBooleanSetter, + "c@:", (IMP)DynamicBooleanGetter, + nil, nil, + NO + }, + { // NSString + "T@\"NSString\"", + "v@:@", (IMP)DynamicStringSetter, + "@@:", (IMP)DynamicStringGetter, + "NSString", nil, + NO + }, + { // NSNumber + "T@\"NSNumber\"", + "v@:@", (IMP)DynamicNumberSetter, + "@@:", (IMP)DynamicNumberGetter, + "NSNumber", nil, + NO + }, + { // GTLDateTime +#if !defined(GTL_TARGET_NAMESPACE) + "T@\"GTLDateTime\"", + "v@:@", (IMP)DynamicDateTimeSetter, + "@@:", (IMP)DynamicDateTimeGetter, + "GTLDateTime", nil, + NO +#else + "T@\"" GTL_TARGET_NAMESPACE_STRING "_" "GTLDateTime\"", + "v@:@", (IMP)DynamicDateTimeSetter, + "@@:", (IMP)DynamicDateTimeGetter, + GTL_TARGET_NAMESPACE_STRING "_" "GTLDateTime", nil, + NO +#endif + }, + { // NSArray with type + "T@\"NSArray\"", + "v@:@", (IMP)DynamicArraySetter, + "@@:", (IMP)DynamicArrayGetter, + "NSArray", nil, + NO + }, + { // id (any of the objects above) + "T@,", + "v@:@", (IMP)DynamicNSObjectSetter, + "@@:", (IMP)DynamicNSObjectGetter, + "NSObject", nil, + NO + }, + { // GTLObject - Last, cause it's a special case and prefix is general + "T@\"", + "v@:@", (IMP)DynamicObjectSetter, + "@@:", (IMP)DynamicObjectGetter, + nil, nil, + YES + }, + }; + + static BOOL hasLookedUpClasses = NO; + if (!hasLookedUpClasses) { + // Unfortunately, you can't put [NSString class] into the static structure, + // so this lookup has to be done at runtime. + hasLookedUpClasses = YES; + for (uint32_t idx = 0; idx < sizeof(kImplInfo)/sizeof(kImplInfo[0]); ++idx) { + if (kImplInfo[idx].returnClassName) { + kImplInfo[idx].returnClass = objc_getClass(kImplInfo[idx].returnClassName); + NSCAssert1(kImplInfo[idx].returnClass != nil, + @"GTLRuntimeCommon: class lookup failed: %s", kImplInfo[idx].returnClassName); + } + } + } + + const char *attr = property_getAttributes(prop); + + const char *dynamicMarker = strstr(attr, ",D"); + if (!dynamicMarker || + (dynamicMarker[2] != 0 && dynamicMarker[2] != ',' )) { + GTL_DEBUG_LOG(@"GTLRuntimeCommon: property %s isn't dynamic, attributes %s", + property_getName(prop), attr ? attr : "(nil)"); + return NULL; + } + + const GTLDynamicImpInfo *result = NULL; + + // Cycle over the list + + for (uint32_t idx = 0; idx < sizeof(kImplInfo)/sizeof(kImplInfo[0]); ++idx) { + const char *attributePrefix = kImplInfo[idx].attributePrefix; + if (strncmp(attr, attributePrefix, strlen(attributePrefix)) == 0) { + result = &kImplInfo[idx]; + if (outReturnClass) *outReturnClass = result->returnClass; + break; + } + } + + if (result == NULL) { + GTL_DEBUG_LOG(@"GTLRuntimeCommon: unexpected attributes %s for property %s", + attr ? attr : "(nil)", property_getName(prop)); + return NULL; + } + + if (result->extractReturnClass && outReturnClass) { + + // add a null at the next quotation mark + char *attrCopy = strdup(attr); + char *classNameStart = attrCopy + 3; + char *classNameEnd = strstr(classNameStart, "\""); + if (classNameEnd) { + *classNameEnd = '\0'; + + // Lookup the return class + *outReturnClass = objc_getClass(classNameStart); + if (*outReturnClass == nil) { + GTL_DEBUG_LOG(@"GTLRuntimeCommon: did not find class with name \"%s\" " + "for property \"%s\" with attributes \"%s\"", + classNameStart, property_getName(prop), attr); + } + } else { + GTL_DEBUG_LOG(@"GTLRuntimeCommon: Failed to find end of class name for " + "property \"%s\" with attributes \"%s\"", + property_getName(prop), attr); + } + free(attrCopy); + } + + return result; +} + +#pragma mark Runtime - wiring point + ++ (BOOL)resolveInstanceMethod:(SEL)sel onClass:(Class)onClass { + // dynamic method resolution: + // http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtDynamicResolution.html + // + // property runtimes: + // http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html + + const char *selName = sel_getName(sel); + size_t selNameLen = strlen(selName); + char lastChar = selName[selNameLen - 1]; + BOOL isSetter = (lastChar == ':'); + + // look for a declared property matching this selector name exactly + Class foundClass = nil; + + objc_property_t prop = PropertyForSel(onClass, sel, isSetter, &foundClass); + if (prop != NULL && foundClass != nil) { + + Class returnClass = nil; + const GTLDynamicImpInfo *implInfo = DynamicImpInfoForProperty(prop, + &returnClass); + if (implInfo == NULL) { + GTL_DEBUG_LOG(@"GTLRuntimeCommon: unexpected return type class %s for " + "property \"%s\" of class \"%s\"", + returnClass ? class_getName(returnClass) : "", + property_getName(prop), + class_getName(onClass)); + } + + if (implInfo != NULL) { + IMP imp = ( isSetter ? implInfo->setterFunction : implInfo->getterFunction ); + const char *encoding = ( isSetter ? implInfo->setterEncoding : implInfo->getterEncoding ); + + class_addMethod(foundClass, sel, imp, encoding); + + const char *propName = property_getName(prop); + NSString *propStr = [NSString stringWithUTF8String:propName]; + + // replace the property name with the proper JSON key if it's + // special-cased with a map in the found class; otherwise, the property + // name is the JSON key + NSDictionary *keyMap = + [[foundClass ancestorClass] propertyToJSONKeyMapForClass:foundClass]; + NSString *jsonKey = [keyMap objectForKey:propStr]; + if (jsonKey == nil) { + jsonKey = propStr; + } + + Class containedClass = nil; + + // For arrays we need to look up what the contained class is. + if (imp == (IMP)DynamicArraySetter || imp == (IMP)DynamicArrayGetter) { + NSDictionary *classMap = + [[foundClass ancestorClass] arrayPropertyToClassMapForClass:foundClass]; + containedClass = [classMap objectForKey:jsonKey]; + if (containedClass == Nil) { + GTL_DEBUG_LOG(@"GTLRuntimeCommon: expected array item class for " + "property \"%s\" of class \"%s\"", + property_getName(prop), class_getName(foundClass)); + } + } + + // save the dispatch info to the cache + [GTLRuntimeCommon setStoredDispatchForClass:foundClass + selector:sel + returnClass:returnClass + containedClass:containedClass + jsonKey:jsonKey]; + return YES; + } + } + + return NO; +} + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLService.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLService.h new file mode 100644 index 0000000000..eac1dac831 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLService.h @@ -0,0 +1,607 @@ +/* Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLService.h +// + +// Service object documentation: +// https://code.google.com/p/google-api-objectivec-client/wiki/Introduction#Services_and_Tickets + +#import + +#import "GTLDefines.h" +#import "GTMHTTPFetcherService.h" +#import "GTLBatchQuery.h" +#import "GTLBatchResult.h" +#import "GTLDateTime.h" +#import "GTLErrorObject.h" +#import "GTLFramework.h" +#import "GTLJSONParser.h" +#import "GTLObject.h" +#import "GTLQuery.h" +#import "GTLUtilities.h" + +#undef _EXTERN +#undef _INITIALIZE_AS +#ifdef GTLSERVICE_DEFINE_GLOBALS +#define _EXTERN +#define _INITIALIZE_AS(x) =x +#else +#define _EXTERN extern +#define _INITIALIZE_AS(x) +#endif + +// Error domains +_EXTERN NSString* const kGTLServiceErrorDomain _INITIALIZE_AS(@"com.google.GTLServiceDomain"); +enum { + kGTLErrorQueryResultMissing = -3000, + kGTLErrorWaitTimedOut = -3001 +}; + +_EXTERN NSString* const kGTLJSONRPCErrorDomain _INITIALIZE_AS(@"com.google.GTLJSONRPCErrorDomain"); + +// We'll consistently store the server error string in the userInfo under +// this key +_EXTERN NSString* const kGTLServerErrorStringKey _INITIALIZE_AS(@"error"); + +_EXTERN Class const kGTLUseRegisteredClass _INITIALIZE_AS(nil); + +_EXTERN NSUInteger const kGTLStandardUploadChunkSize _INITIALIZE_AS(NSUIntegerMax); + +// When servers return us structured JSON errors, the NSError will +// contain a GTLErrorObject in the userInfo dictionary under the key +// kGTLStructuredErrorsKey +_EXTERN NSString* const kGTLStructuredErrorKey _INITIALIZE_AS(@"GTLStructuredError"); + +// When specifying an ETag for updating or deleting a single entry, use +// kGTLETagWildcard to tell the server to replace the current value +// unconditionally. Do not use this in entries in a batch feed. +_EXTERN NSString* const kGTLETagWildcard _INITIALIZE_AS(@"*"); + +// Notifications when parsing of a fetcher feed or entry begins or ends +_EXTERN NSString* const kGTLServiceTicketParsingStartedNotification _INITIALIZE_AS(@"kGTLServiceTicketParsingStartedNotification"); +_EXTERN NSString* const kGTLServiceTicketParsingStoppedNotification _INITIALIZE_AS(@"kGTLServiceTicketParsingStoppedNotification"); + +@class GTLServiceTicket; + +// Block types used for fetch callbacks +// +// These typedefs are not used in the header file method declarations +// since it's more useful when code sense expansions show the argument +// types rather than the typedefs + +#if NS_BLOCKS_AVAILABLE +typedef void (^GTLServiceCompletionHandler)(GTLServiceTicket *ticket, id object, NSError *error); + +typedef void (^GTLServiceUploadProgressBlock)(GTLServiceTicket *ticket, unsigned long long numberOfBytesRead, unsigned long long dataLength); +#else +typedef void *GTLServiceCompletionHandler; + +typedef void *GTLServiceUploadProgressBlock; +#endif // NS_BLOCKS_AVAILABLE + +#pragma mark - + +// +// Service base class +// + +@interface GTLService : NSObject { + @private + NSOperationQueue *parseQueue_; + NSString *userAgent_; + GTMHTTPFetcherService *fetcherService_; + NSString *userAgentAddition_; + + NSMutableDictionary *serviceProperties_; // initial values for properties in future tickets + + NSDictionary *surrogates_; // initial value for surrogates in future tickets + + SEL uploadProgressSelector_; // optional + +#if NS_BLOCKS_AVAILABLE + BOOL (^retryBlock_)(GTLServiceTicket *, BOOL, NSError *); + void (^uploadProgressBlock_)(GTLServiceTicket *ticket, + unsigned long long numberOfBytesRead, + unsigned long long dataLength); +#elif !__LP64__ + // Placeholders: for 32-bit builds, keep the size of the object's ivar section + // the same with and without blocks + id retryPlaceholder_; + id uploadProgressPlaceholder_; +#endif + + NSUInteger uploadChunkSize_; // zero when uploading via multi-part MIME http body + + BOOL isRetryEnabled_; // user allows auto-retries + SEL retrySelector_; // optional; set with setServiceRetrySelector + NSTimeInterval maxRetryInterval_; // default to 600. seconds + + BOOL shouldFetchNextPages_; + + NSString *apiKey_; + BOOL isRESTDataWrapperRequired_; + NSString *apiVersion_; + NSURL *rpcURL_; + NSURL *rpcUploadURL_; + NSDictionary *urlQueryParameters_; + NSDictionary *additionalHTTPHeaders_; +} + +#pragma mark Query Execution + +// The finishedSelector has a signature matching: +// +// - (void)serviceTicket:(GTLServiceTicket *)ticket +// finishedWithObject:(GTLObject *)object +// error:(NSError *)error +// +// If an error occurs, the error parameter will be non-nil. Otherwise, +// the object parameter will point to a GTLObject, if any was returned by +// the fetch. (Delete fetches return no object, so the second parameter will +// be nil.) +// +// If the query object is a GTLBatchQuery, the object passed to the callback +// will be a GTLBatchResult; see the batch query documentation: +// https://code.google.com/p/google-api-objectivec-client/wiki/Introduction#Batch_Operations + +- (GTLServiceTicket *)executeQuery:(id)query + delegate:(id)delegate + didFinishSelector:(SEL)finishedSelector GTL_NONNULL((1)); + +#if NS_BLOCKS_AVAILABLE +- (GTLServiceTicket *)executeQuery:(id)query + completionHandler:(void (^)(GTLServiceTicket *ticket, id object, NSError *error))handler GTL_NONNULL((1)); +#endif + +// Automatic page fetches +// +// Tickets can optionally do a sequence of fetches for queries where +// repeated requests with nextPageToken or nextStartIndex values is required to +// retrieve items of all pages of the response collection. The client's +// callback is invoked only when all items have been retrieved, or an error has +// occurred. During the fetch, the items accumulated so far are available from +// the ticket. +// +// Note that the final object may be a combination of multiple page responses +// so it may not be the same as if all results had been returned in a single +// page. Some fields of the response such as total item counts may reflect only +// the final page's values. +// +// Automatic page fetches will return an error if more than 25 page fetches are +// required. For debug builds, this will log a warning to the console when more +// than 2 page fetches occur, as a reminder that the query's maxResults +// parameter should probably be increased to specify more items returned per +// page. +// +// Default value is NO. +@property (nonatomic, assign) BOOL shouldFetchNextPages; + +// Retrying; see comments on retry support at the top of GTMHTTPFetcher. +// +// Default value is NO. +@property (nonatomic, assign, getter=isRetryEnabled) BOOL retryEnabled; + +// Some services require a developer key for quotas and limits. Setting this +// will include it on all request sent to this service via a GTLQuery class. +@property (nonatomic, copy) NSString *APIKey; + +// An authorizer adds user authentication headers to the request as needed. +@property (nonatomic, retain) id authorizer; + +// Retry selector is optional for retries. +// +// If present, it should have the signature: +// -(BOOL)ticket:(GTLServiceTicket *)ticket willRetry:(BOOL)suggestedWillRetry forError:(NSError *)error +// and return YES to cause a retry. Note that unlike the GTMHTTPFetcher retry +// selector, this selector's first argument is a ticket, not a fetcher. + +@property (nonatomic, assign) SEL retrySelector; +#if NS_BLOCKS_AVAILABLE +@property (copy) BOOL (^retryBlock)(GTLServiceTicket *ticket, BOOL suggestedWillRetry, NSError *error); +#endif + +@property (nonatomic, assign) NSTimeInterval maxRetryInterval; + +// +// Fetches may be done using RPC or REST APIs, without creating +// a GTLQuery object +// + +#pragma mark RPC Fetch Methods + +// +// These methods may be used for RPC fetches without creating a GTLQuery object +// + +- (GTLServiceTicket *)fetchObjectWithMethodNamed:(NSString *)methodName + parameters:(NSDictionary *)parameters + objectClass:(Class)objectClass + delegate:(id)delegate + didFinishSelector:(SEL)finishedSelector GTL_NONNULL((1)); + +- (GTLServiceTicket *)fetchObjectWithMethodNamed:(NSString *)methodName + insertingObject:(GTLObject *)bodyObject + objectClass:(Class)objectClass + delegate:(id)delegate + didFinishSelector:(SEL)finishedSelector GTL_NONNULL((1)); + +- (GTLServiceTicket *)fetchObjectWithMethodNamed:(NSString *)methodName + parameters:(NSDictionary *)parameters + insertingObject:(GTLObject *)bodyObject + objectClass:(Class)objectClass + delegate:(id)delegate + didFinishSelector:(SEL)finishedSelector GTL_NONNULL((1)); + +#if NS_BLOCKS_AVAILABLE +- (GTLServiceTicket *)fetchObjectWithMethodNamed:(NSString *)methodName + parameters:(NSDictionary *)parameters + objectClass:(Class)objectClass + completionHandler:(void (^)(GTLServiceTicket *ticket, id object, NSError *error))handler GTL_NONNULL((1)); + +- (GTLServiceTicket *)fetchObjectWithMethodNamed:(NSString *)methodName + insertingObject:(GTLObject *)bodyObject + objectClass:(Class)objectClass + completionHandler:(void (^)(GTLServiceTicket *ticket, id object, NSError *error))handler GTL_NONNULL((1)); + +- (GTLServiceTicket *)fetchObjectWithMethodNamed:(NSString *)methodName + parameters:(NSDictionary *)parameters + insertingObject:(GTLObject *)bodyObject + objectClass:(Class)objectClass + completionHandler:(void (^)(GTLServiceTicket *ticket, id object, NSError *error))handler GTL_NONNULL((1)); +#endif + +#pragma mark REST Fetch Methods + +- (GTLServiceTicket *)fetchObjectWithURL:(NSURL *)objectURL + delegate:(id)delegate + didFinishSelector:(SEL)finishedSelector GTL_NONNULL((1)); + +- (GTLServiceTicket *)fetchObjectWithURL:(NSURL *)objectURL + objectClass:(Class)objectClass + delegate:(id)delegate + didFinishSelector:(SEL)finishedSelector GTL_NONNULL((1)); + +- (GTLServiceTicket *)fetchPublicObjectWithURL:(NSURL *)objectURL + objectClass:(Class)objectClass + delegate:(id)delegate + didFinishSelector:(SEL)finishedSelector GTL_NONNULL((1)); + +- (GTLServiceTicket *)fetchObjectByInsertingObject:(GTLObject *)bodyToPut + forURL:(NSURL *)destinationURL + delegate:(id)delegate + didFinishSelector:(SEL)finishedSelector GTL_NONNULL((1,2)); + +- (GTLServiceTicket *)fetchObjectByUpdatingObject:(GTLObject *)bodyToPut + forURL:(NSURL *)destinationURL + delegate:(id)delegate + didFinishSelector:(SEL)finishedSelector GTL_NONNULL((1,2)); + +- (GTLServiceTicket *)deleteResourceURL:(NSURL *)destinationURL + ETag:(NSString *)etagOrNil + delegate:(id)delegate + didFinishSelector:(SEL)finishedSelector GTL_NONNULL((1)); + +#if NS_BLOCKS_AVAILABLE +- (GTLServiceTicket *)fetchObjectWithURL:(NSURL *)objectURL + completionHandler:(void (^)(GTLServiceTicket *ticket, id object, NSError *error))handler GTL_NONNULL((1)); + +- (GTLServiceTicket *)fetchObjectByInsertingObject:(GTLObject *)bodyToPut + forURL:(NSURL *)destinationURL + completionHandler:(void (^)(GTLServiceTicket *ticket, id object, NSError *error))handler GTL_NONNULL((1)); + +- (GTLServiceTicket *)fetchObjectByUpdatingObject:(GTLObject *)bodyToPut + forURL:(NSURL *)destinationURL + completionHandler:(void (^)(GTLServiceTicket *ticket, id object, NSError *error))handler GTL_NONNULL((1)); + +- (GTLServiceTicket *)deleteResourceURL:(NSURL *)destinationURL + ETag:(NSString *)etagOrNil + completionHandler:(void (^)(GTLServiceTicket *ticket, id object, NSError *error))handler GTL_NONNULL((1)); +#endif + +#pragma mark User Properties + +// Properties and userData are supported for client convenience. +// +// Property keys beginning with _ are reserved by the library. +// +// The service properties dictionary is copied to become the initial property +// dictionary for each ticket. +- (void)setServiceProperty:(id)obj forKey:(NSString *)key GTL_NONNULL((2)); // pass nil obj to remove property +- (id)servicePropertyForKey:(NSString *)key GTL_NONNULL((1)); + +@property (nonatomic, copy) NSDictionary *serviceProperties; + +// The service userData becomes the initial value for each future ticket's +// userData. +@property (nonatomic, retain) id serviceUserData; + +#pragma mark Request Settings + +// Set the surrogates to be used for future tickets. Surrogates are subclasses +// to be used instead of standard classes when creating objects from the JSON. +// For example, this code will make the framework generate objects +// using MyCalendarItemSubclass instead of GTLItemCalendar and +// MyCalendarEventSubclass instead of GTLItemCalendarEvent. +// +// NSDictionary *surrogates = [NSDictionary dictionaryWithObjectsAndKeys: +// [MyCalendarEntrySubclass class], [GTLItemCalendar class], +// [MyCalendarEventSubclass class], [GTLItemCalendarEvent class], +// nil]; +// [calendarService setServiceSurrogates:surrogates]; +// +@property (nonatomic, retain) NSDictionary *surrogates; + +// On iOS 4 and later, the fetch may optionally continue in the background +// until finished or stopped by OS expiration. +// +// The default value is NO. +// +// For Mac OS X, background fetches are always supported, and this property +// is ignored. +@property (nonatomic, assign) BOOL shouldFetchInBackground; + +// Callbacks can be invoked on an operation queue rather than via the run loop +// starting on 10.7 and iOS 6. Do not specify both run loop modes and an +// operation queue. Specifying a delegate queue typically looks like this: +// +// service.delegateQueue = [[[NSOperationQueue alloc] init] autorelease]; +// +// Since the callbacks will be on a thread of the operation queue, the client +// may re-dispatch from the callbacks to a known dispatch queue or to the +// main queue. +@property (nonatomic, retain) NSOperationQueue *delegateQueue; + +// Run loop modes are used for scheduling NSURLConnections. +// +// The default value, nil, schedules connections using the current run +// loop mode. To use the service during a modal dialog, be sure to specify +// NSModalPanelRunLoopMode as one of the modes. +@property (nonatomic, retain) NSArray *runLoopModes; + +// Applications needing an additional identifier in the server logs may specify +// one. +@property (nonatomic, copy) NSString *userAgentAddition; + +// Applications have a default user-agent based on the application signature +// in the Info.plist settings. Most applications should not explicitly set +// this property. +@property (nonatomic, copy) NSString *userAgent; + +// The request user agent includes the library and OS version appended to the +// base userAgent, along with the optional addition string. +@property (nonatomic, readonly) NSString *requestUserAgent; + +// Applications may call requestForURL:httpMethod to get a request with the +// proper user-agent and ETag headers +// +// For http method, pass nil (for default GET method), POST, PUT, or DELETE +- (NSMutableURLRequest *)requestForURL:(NSURL *)url + ETag:(NSString *)etagOrNil + httpMethod:(NSString *)httpMethodOrNil GTL_NONNULL((1)); + +// objectRequestForURL returns an NSMutableURLRequest for a JSON GTL object +// +// The object is the object being sent to the server, or nil; +// the http method may be nil for GET, or POST, PUT, DELETE +- (NSMutableURLRequest *)objectRequestForURL:(NSURL *)url + object:(GTLObject *)object + ETag:(NSString *)etag + httpMethod:(NSString *)httpMethod + isREST:(BOOL)isREST + additionalHeaders:(NSDictionary *)additionalHeaders + ticket:(GTLServiceTicket *)ticket GTL_NONNULL((1)); + +// The queue used for parsing JSON responses (previously this property +// was called operationQueue) +@property (nonatomic, retain) NSOperationQueue *parseQueue; + +// The fetcher service object issues the GTMHTTPFetcher instances +// for this API service +@property (nonatomic, retain) GTMHTTPFetcherService *fetcherService; + +// Default storage for cookies is in the service object's fetchHistory. +// +// Apps that want to share cookies between all standalone fetchers and the +// service object may specify static application-wide cookie storage, +// kGTMHTTPFetcherCookieStorageMethodStatic. +@property (nonatomic, assign) NSInteger cookieStorageMethod; + +// When sending REST style queries, should the payload be wrapped in a "data" +// element, and will the reply be wrapped in an "data" element. +@property (nonatomic, assign) BOOL isRESTDataWrapperRequired; + +// Any url query parameters to add to urls (useful for debugging with some +// services). +@property (copy) NSDictionary *urlQueryParameters; + +// Any extra http headers to set on requests for GTLObjects. +@property (copy) NSDictionary *additionalHTTPHeaders; + +// The service API version. +@property (nonatomic, copy) NSString *apiVersion; + +// The URL for sending RPC requests for this service. +@property (nonatomic, retain) NSURL *rpcURL; + +// The URL for sending RPC requests which initiate file upload. +@property (nonatomic, retain) NSURL *rpcUploadURL; + +// Set a non-zero value to enable uploading via chunked fetches +// (resumable uploads); typically this defaults to kGTLStandardUploadChunkSize +// for service subclasses that support chunked uploads +@property (nonatomic, assign) NSUInteger serviceUploadChunkSize; + +// Service subclasses may specify their own default chunk size ++ (NSUInteger)defaultServiceUploadChunkSize; + +// The service uploadProgressSelector becomes the initial value for each future +// ticket's uploadProgressSelector. +// +// The optional uploadProgressSelector will be called in the delegate as bytes +// are uploaded to the server. It should have a signature matching +// +// - (void)ticket:(GTLServiceTicket *)ticket +// hasDeliveredByteCount:(unsigned long long)numberOfBytesRead +// ofTotalByteCount:(unsigned long long)dataLength; +@property (nonatomic, assign) SEL uploadProgressSelector; + +#if NS_BLOCKS_AVAILABLE +@property (copy) void (^uploadProgressBlock)(GTLServiceTicket *ticket, unsigned long long numberOfBytesRead, unsigned long long dataLength); +#endif + +// Wait synchronously for fetch to complete (strongly discouraged) +// +// This just runs the current event loop until the fetch completes +// or the timout limit is reached. This may discard unexpected events +// that occur while spinning, so it's really not appropriate for use +// in serious applications. +// +// Returns true if an object was successfully fetched. If the wait +// timed out, returns false and the returned error is nil. +// +// The returned object or error, if any, will be already autoreleased +// +// This routine will likely be removed in some future releases of the library. +- (BOOL)waitForTicket:(GTLServiceTicket *)ticket + timeout:(NSTimeInterval)timeoutInSeconds + fetchedObject:(GTLObject **)outObjectOrNil + error:(NSError **)outErrorOrNil GTL_NONNULL((1)); +@end + +#pragma mark - + +// +// Ticket base class +// +@interface GTLServiceTicket : NSObject { + GTLService *service_; + + NSMutableDictionary *ticketProperties_; + NSDictionary *surrogates_; + + GTMHTTPFetcher *objectFetcher_; + SEL uploadProgressSelector_; + BOOL shouldFetchNextPages_; + BOOL isRetryEnabled_; + SEL retrySelector_; + NSTimeInterval maxRetryInterval_; + +#if NS_BLOCKS_AVAILABLE + BOOL (^retryBlock_)(GTLServiceTicket *, BOOL, NSError *); + void (^uploadProgressBlock_)(GTLServiceTicket *ticket, + unsigned long long numberOfBytesRead, + unsigned long long dataLength); +#elif !__LP64__ + // Placeholders: for 32-bit builds, keep the size of the object's ivar section + // the same with and without blocks + id retryPlaceholder_; + id uploadProgressPlaceholder_; +#endif + + GTLObject *postedObject_; + GTLObject *fetchedObject_; + id executingQuery_; + id originalQuery_; + NSError *fetchError_; + BOOL hasCalledCallback_; + NSUInteger pagesFetchedCounter_; + + NSString *apiKey_; + BOOL isREST_; + + NSOperation *parseOperation_; +} + ++ (id)ticketForService:(GTLService *)service; + +- (id)initWithService:(GTLService *)service; + +- (id)service; + +#pragma mark Execution Control +// if cancelTicket is called, the fetch is stopped if it is in progress, +// the callbacks will not be called, and the ticket will no longer be useful +// (though the client must still release the ticket if it retained the ticket) +- (void)cancelTicket; + +// chunked upload tickets may be paused +- (void)pauseUpload; +- (void)resumeUpload; +- (BOOL)isUploadPaused; + +@property (nonatomic, retain) GTMHTTPFetcher *objectFetcher; +@property (nonatomic, assign) SEL uploadProgressSelector; + +// Services which do not require an user authorization may require a developer +// API key for quota management +@property (nonatomic, copy) NSString *APIKey; + +#pragma mark User Properties + +// Properties and userData are supported for client convenience. +// +// Property keys beginning with _ are reserved by the library. +- (void)setProperty:(id)obj forKey:(NSString *)key GTL_NONNULL((1)); // pass nil obj to remove property +- (id)propertyForKey:(NSString *)key; + +@property (nonatomic, copy) NSDictionary *properties; +@property (nonatomic, retain) id userData; + +#pragma mark Payload + +@property (nonatomic, retain) GTLObject *postedObject; +@property (nonatomic, retain) GTLObject *fetchedObject; +@property (nonatomic, retain) id executingQuery; // Query currently being fetched by this ticket +@property (nonatomic, retain) id originalQuery; // Query used to create this ticket +- (GTLQuery *)queryForRequestID:(NSString *)requestID GTL_NONNULL((1)); // Returns the query from within the batch with the given id. + +@property (nonatomic, retain) NSDictionary *surrogates; + +#pragma mark Retry + +@property (nonatomic, assign, getter=isRetryEnabled) BOOL retryEnabled; +@property (nonatomic, assign) SEL retrySelector; +#if NS_BLOCKS_AVAILABLE +@property (copy) BOOL (^retryBlock)(GTLServiceTicket *ticket, BOOL suggestedWillRetry, NSError *error); +#endif +@property (nonatomic, assign) NSTimeInterval maxRetryInterval; + +#pragma mark Status + +@property (nonatomic, readonly) NSInteger statusCode; // server status from object fetch +@property (nonatomic, retain) NSError *fetchError; +@property (nonatomic, assign) BOOL hasCalledCallback; + +#pragma mark Pagination + +@property (nonatomic, assign) BOOL shouldFetchNextPages; +@property (nonatomic, assign) NSUInteger pagesFetchedCounter; + +#pragma mark Upload + +#if NS_BLOCKS_AVAILABLE +@property (copy) void (^uploadProgressBlock)(GTLServiceTicket *ticket, unsigned long long numberOfBytesRead, unsigned long long dataLength); +#endif + +@end + + +// Category to provide opaque access to tickets stored in fetcher properties +@interface GTMHTTPFetcher (GTLServiceTicketAdditions) +- (id)ticket; +@end + diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLService.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLService.m new file mode 100644 index 0000000000..ad00bb1df3 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLService.m @@ -0,0 +1,2407 @@ +/* Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLService.m +// + +#import +#if TARGET_OS_MAC +#include +#endif + +#if TARGET_OS_IPHONE +#import +#endif + +#define GTLSERVICE_DEFINE_GLOBALS 1 +#import "GTLService.h" + +static NSString *const kUserDataPropertyKey = @"_userData"; + +static NSString* const kFetcherDelegateKey = @"_delegate"; +static NSString* const kFetcherObjectClassKey = @"_objectClass"; +static NSString* const kFetcherFinishedSelectorKey = @"_finishedSelector"; +static NSString* const kFetcherCompletionHandlerKey = @"_completionHandler"; +static NSString* const kFetcherTicketKey = @"_ticket"; +static NSString* const kFetcherFetchErrorKey = @"_fetchError"; +static NSString* const kFetcherParsingNotificationKey = @"_parseNotification"; +static NSString* const kFetcherParsedObjectKey = @"_parsedObject"; +static NSString* const kFetcherBatchClassMapKey = @"_batchClassMap"; +static NSString* const kFetcherCallbackThreadKey = @"_callbackThread"; +static NSString* const kFetcherCallbackRunLoopModesKey = @"_runLoopModes"; + +static const NSUInteger kMaxNumberOfNextPagesFetched = 25; + +// we'll enforce 50K chunks minimum just to avoid the server getting hit +// with too many small upload chunks +static const NSUInteger kMinimumUploadChunkSize = 50000; +static const NSUInteger kStandardUploadChunkSize = NSUIntegerMax; + +// Helper to get the ETag if it is defined on an object. +static NSString *ETagIfPresent(GTLObject *obj) { + NSString *result = [obj.JSON objectForKey:@"etag"]; + return result; +} + +@interface GTLServiceTicket () +@property (retain) NSOperation *parseOperation; +@property (assign) BOOL isREST; +@end + +// category to provide opaque access to tickets stored in fetcher properties +@implementation GTMHTTPFetcher (GTLServiceTicketAdditions) +- (id)ticket { + return [self propertyForKey:kFetcherTicketKey]; +} +@end + +// If GTMHTTPUploadFetcher is available, it can be used for chunked uploads +// +// We locally declare some methods of GTMHTTPUploadFetcher so we +// do not need to import the header, as some projects may not have it available +@interface GTMHTTPUploadFetcher : GTMHTTPFetcher ++ (GTMHTTPUploadFetcher *)uploadFetcherWithRequest:(NSURLRequest *)request + uploadData:(NSData *)data + uploadMIMEType:(NSString *)uploadMIMEType + chunkSize:(NSUInteger)chunkSize + fetcherService:(GTMHTTPFetcherService *)fetcherService; ++ (GTMHTTPUploadFetcher *)uploadFetcherWithRequest:(NSURLRequest *)request + uploadFileHandle:(NSFileHandle *)uploadFileHandle + uploadMIMEType:(NSString *)uploadMIMEType + chunkSize:(NSUInteger)chunkSize + fetcherService:(GTMHTTPFetcherService *)fetcherService; ++ (GTMHTTPUploadFetcher *)uploadFetcherWithLocation:(NSURL *)location + uploadFileHandle:(NSFileHandle *)fileHandle + uploadMIMEType:(NSString *)uploadMIMEType + chunkSize:(NSUInteger)chunkSize + fetcherService:(GTMHTTPFetcherService *)fetcherService; +- (void)pauseFetching; +- (void)resumeFetching; +- (BOOL)isPaused; +@end + + +@interface GTLService () +- (void)prepareToParseObjectForFetcher:(GTMHTTPFetcher *)fetcher; +- (void)handleParsedObjectForFetcher:(GTMHTTPFetcher *)fetcher; +- (BOOL)fetchNextPageWithQuery:(GTLQuery *)query + delegate:(id)delegate + didFinishedSelector:(SEL)finishedSelector + completionHandler:(GTLServiceCompletionHandler)completionHandler + ticket:(GTLServiceTicket *)ticket; +- (id )nextPageQueryForQuery:(GTLQuery *)query + result:(GTLObject *)object + ticket:(GTLServiceTicket *)ticket; +- (GTLObject *)mergedNewResultObject:(GTLObject *)newResult + oldResultObject:(GTLObject *)oldResult + forQuery:(GTLQuery *)query; +- (GTMHTTPUploadFetcher *)uploadFetcherWithRequest:(NSURLRequest *)request + fetcherService:(GTMHTTPFetcherService *)fetcherService + params:(GTLUploadParameters *)uploadParams; ++ (void)invokeCallback:(SEL)callbackSel + target:(id)target + ticket:(id)ticket + object:(id)object + error:(id)error; +- (BOOL)invokeRetrySelector:(SEL)retrySelector + delegate:(id)delegate + ticket:(GTLServiceTicket *)ticket + willRetry:(BOOL)willRetry + error:(NSError *)error; +- (BOOL)objectFetcher:(GTMHTTPFetcher *)fetcher + willRetry:(BOOL)willRetry + forError:(NSError *)error; +- (void)objectFetcher:(GTMHTTPFetcher *)fetcher + finishedWithData:(NSData *)data + error:(NSError *)error; +- (void)parseObjectFromDataOfFetcher:(GTMHTTPFetcher *)fetcher; +@end + +@interface GTLObject (StandardProperties) +@property (retain) NSString *ETag; +@property (retain) NSString *nextPageToken; +@property (retain) NSNumber *nextStartIndex; +@end + +@implementation GTLService + +@synthesize userAgentAddition = userAgentAddition_, + fetcherService = fetcherService_, + parseQueue = parseQueue_, + shouldFetchNextPages = shouldFetchNextPages_, + surrogates = surrogates_, + uploadProgressSelector = uploadProgressSelector_, + retryEnabled = isRetryEnabled_, + retrySelector = retrySelector_, + maxRetryInterval = maxRetryInterval_, + APIKey = apiKey_, + isRESTDataWrapperRequired = isRESTDataWrapperRequired_, + urlQueryParameters = urlQueryParameters_, + additionalHTTPHeaders = additionalHTTPHeaders_, + apiVersion = apiVersion_, + rpcURL = rpcURL_, + rpcUploadURL = rpcUploadURL_; + +#if NS_BLOCKS_AVAILABLE +@synthesize retryBlock = retryBlock_, + uploadProgressBlock = uploadProgressBlock_; +#endif + ++ (Class)ticketClass { + return [GTLServiceTicket class]; +} + +- (id)init { + self = [super init]; + if (self) { + +#if GTL_IPHONE || (MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5) + // For 10.6 and up, always use an operation queue + parseQueue_ = [[NSOperationQueue alloc] init]; +#elif !GTL_SKIP_PARSE_THREADING + // Avoid NSOperationQueue prior to 10.5.6, per + // http://www.mikeash.com/?page=pyblog/use-nsoperationqueue.html + SInt32 bcdSystemVersion = 0; + (void) Gestalt(gestaltSystemVersion, &bcdSystemVersion); + + if (bcdSystemVersion >= 0x1057) { + parseQueue_ = [[NSOperationQueue alloc] init]; + } +#else + // parseQueue_ defaults to nil, so parsing will be done immediately + // on the current thread +#endif + + fetcherService_ = [[GTMHTTPFetcherService alloc] init]; + + NSUInteger chunkSize = [[self class] defaultServiceUploadChunkSize]; + self.serviceUploadChunkSize = chunkSize; + } + return self; +} + +- (void)dealloc { + [parseQueue_ release]; + [userAgent_ release]; + [fetcherService_ release]; + [userAgentAddition_ release]; + [serviceProperties_ release]; + [surrogates_ release]; +#if NS_BLOCKS_AVAILABLE + [uploadProgressBlock_ release]; + [retryBlock_ release]; +#endif + [apiKey_ release]; + [apiVersion_ release]; + [rpcURL_ release]; + [rpcUploadURL_ release]; + [urlQueryParameters_ release]; + [additionalHTTPHeaders_ release]; + + [super dealloc]; +} + +- (NSString *)requestUserAgent { + NSString *userAgent = self.userAgent; + if ([userAgent length] == 0) { + // the service instance is missing an explicit user-agent; use the bundle ID + // or process name + NSBundle *owningBundle = [NSBundle bundleForClass:[self class]]; + if (owningBundle == nil + || [[owningBundle bundleIdentifier] isEqual:@"com.google.GTLFramework"]) { + + owningBundle = [NSBundle mainBundle]; + } + + userAgent = GTMApplicationIdentifier(owningBundle); + } + + NSString *requestUserAgent = userAgent; + + // if the user agent already specifies the library version, we'll + // use it verbatim in the request + NSString *libraryString = @"google-api-objc-client"; + NSRange libRange = [userAgent rangeOfString:libraryString + options:NSCaseInsensitiveSearch]; + if (libRange.location == NSNotFound) { + // the user agent doesn't specify the client library, so append that + // information, and the system version + NSString *libVersionString = GTLFrameworkVersionString(); + + NSString *systemString = GTMSystemVersionString(); + + // We don't clean this with GTMCleanedUserAgentString so spaces are + // preserved + NSString *userAgentAddition = self.userAgentAddition; + NSString *customString = userAgentAddition ? + [@" " stringByAppendingString:userAgentAddition] : @""; + + // Google servers look for gzip in the user agent before sending gzip- + // encoded responses. See Service.java + requestUserAgent = [NSString stringWithFormat:@"%@ %@/%@ %@%@ (gzip)", + userAgent, libraryString, libVersionString, systemString, customString]; + } + return requestUserAgent; +} + +- (NSMutableURLRequest *)requestForURL:(NSURL *)url + ETag:(NSString *)etag + httpMethod:(NSString *)httpMethod + ticket:(GTLServiceTicket *)ticket { + + // subclasses may add headers to this + NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] initWithURL:url + cachePolicy:NSURLRequestReloadIgnoringCacheData + timeoutInterval:60] autorelease]; + NSString *requestUserAgent = self.requestUserAgent; + [request setValue:requestUserAgent forHTTPHeaderField:@"User-Agent"]; + + if ([httpMethod length] > 0) { + [request setHTTPMethod:httpMethod]; + } + + if ([etag length] > 0) { + + // it's rather unexpected for an etagged object to be provided for a GET, + // but we'll check for an etag anyway, similar to HttpGDataRequest.java, + // and if present use it to request only an unchanged resource + + BOOL isDoingHTTPGet = (httpMethod == nil + || [httpMethod caseInsensitiveCompare:@"GET"] == NSOrderedSame); + + if (isDoingHTTPGet) { + + // set the etag header, even if weak, indicating we don't want + // another copy of the resource if it's the same as the object + [request setValue:etag forHTTPHeaderField:@"If-None-Match"]; + + } else { + + // if we're doing PUT or DELETE, set the etag header indicating + // we only want to update the resource if our copy matches the current + // one (unless the etag is weak and so shouldn't be a constraint at all) + BOOL isWeakETag = [etag hasPrefix:@"W/"]; + + BOOL isModifying = + [httpMethod caseInsensitiveCompare:@"PUT"] == NSOrderedSame + || [httpMethod caseInsensitiveCompare:@"DELETE"] == NSOrderedSame + || [httpMethod caseInsensitiveCompare:@"PATCH"] == NSOrderedSame; + + if (isModifying && !isWeakETag) { + [request setValue:etag forHTTPHeaderField:@"If-Match"]; + } + } + } + + return request; +} + +- (NSMutableURLRequest *)requestForURL:(NSURL *)url + ETag:(NSString *)etag + httpMethod:(NSString *)httpMethod { + // this public entry point authenticates from the service object but + // not from the auth token in the ticket + return [self requestForURL:url ETag:etag httpMethod:httpMethod ticket:nil]; +} + +// objectRequestForURL returns an NSMutableURLRequest for a GTLObject +// +// the object is the object being sent to the server, or nil; +// the http method may be nil for get, or POST, PUT, DELETE + +- (NSMutableURLRequest *)objectRequestForURL:(NSURL *)url + object:(GTLObject *)object + ETag:(NSString *)etag + httpMethod:(NSString *)httpMethod + isREST:(BOOL)isREST + additionalHeaders:(NSDictionary *)additionalHeaders + ticket:(GTLServiceTicket *)ticket { + if (object) { + // if the object being sent has an etag, add it to the request header to + // avoid retrieving a duplicate or to avoid writing over an updated + // version of the resource on the server + // + // Typically, delete requests will provide an explicit ETag parameter, and + // other requests will have the ETag carried inside the object being updated + if (etag == nil) { + SEL selEtag = @selector(ETag); + if ([object respondsToSelector:selEtag]) { + etag = [object performSelector:selEtag]; + } + } + } + + NSMutableURLRequest *request = [self requestForURL:url + ETag:etag + httpMethod:httpMethod + ticket:ticket]; + NSString *acceptValue; + NSString *contentTypeValue; + if (isREST) { + acceptValue = @"application/json"; + contentTypeValue = @"application/json; charset=utf-8"; + } else { + acceptValue = @"application/json-rpc"; + contentTypeValue = @"application/json-rpc; charset=utf-8"; + } + [request setValue:acceptValue forHTTPHeaderField:@"Accept"]; + [request setValue:contentTypeValue forHTTPHeaderField:@"Content-Type"]; + + [request setValue:@"no-cache" forHTTPHeaderField:@"Cache-Control"]; + + // Add the additional http headers from the service, and then from the query + NSDictionary *headers = self.additionalHTTPHeaders; + for (NSString *key in headers) { + NSString *value = [headers valueForKey:key]; + [request setValue:value forHTTPHeaderField:key]; + } + + headers = additionalHeaders; + for (NSString *key in headers) { + NSString *value = [headers valueForKey:key]; + [request setValue:value forHTTPHeaderField:key]; + } + + return request; +} + +#pragma mark - + +// common fetch starting method + +- (GTLServiceTicket *)fetchObjectWithURL:(NSURL *)targetURL + objectClass:(Class)objectClass + bodyObject:(GTLObject *)bodyObject + dataToPost:(NSData *)dataToPost + ETag:(NSString *)etag + httpMethod:(NSString *)httpMethod + mayAuthorize:(BOOL)mayAuthorize + isREST:(BOOL)isREST + delegate:(id)delegate + didFinishSelector:(SEL)finishedSelector + completionHandler:(id)completionHandler // GTLServiceCompletionHandler + executingQuery:(id)query + ticket:(GTLServiceTicket *)ticket { + + GTMAssertSelectorNilOrImplementedWithArgs(delegate, finishedSelector, @encode(GTLServiceTicket *), @encode(GTLObject *), @encode(NSError *), 0); + + // The completionHandler argument is declared as an id, not as a block + // pointer, so this can be built with the 10.6 SDK and still run on 10.5. + // If the argument were declared as a block pointer, the invocation for + // fetchObjectWithURL: created in GTLService would cause an exception + // since 10.5's NSInvocation cannot deal with encoding of block pointers. + + GTL_DEBUG_ASSERT(targetURL != nil, @"no url?"); + if (targetURL == nil) return nil; + + // we need to create a ticket unless one was created earlier (like during + // authentication) + if (!ticket) { + ticket = [[[self class] ticketClass] ticketForService:self]; + } + + ticket.isREST = isREST; + + // Add any service specific query parameters. + NSDictionary *urlQueryParameters = self.urlQueryParameters; + if ([urlQueryParameters count] > 0) { + targetURL = [GTLUtilities URLWithString:[targetURL absoluteString] + queryParameters:urlQueryParameters]; + } + + // If this is REST and there is a developer key, add it onto the url. RPC + // adds the key into the payload, not on the url. + NSString *apiKey = self.APIKey; + if (isREST && [apiKey length] > 0) { + NSString *const kDeveloperAPIQueryParamKey = @"key"; + NSDictionary *queryParameters; + queryParameters = [NSDictionary dictionaryWithObject:apiKey + forKey:kDeveloperAPIQueryParamKey]; + targetURL = [GTLUtilities URLWithString:[targetURL absoluteString] + queryParameters:queryParameters]; + } + + NSDictionary *additionalHeaders = query.additionalHTTPHeaders; + + NSMutableURLRequest *request = [self objectRequestForURL:targetURL + object:bodyObject + ETag:etag + httpMethod:httpMethod + isREST:isREST + additionalHeaders:additionalHeaders + ticket:ticket]; + + GTMAssertSelectorNilOrImplementedWithArgs(delegate, ticket.uploadProgressSelector, + @encode(GTLServiceTicket *), @encode(unsigned long long), + @encode(unsigned long long), 0); + GTMAssertSelectorNilOrImplementedWithArgs(delegate, ticket.retrySelector, + @encode(GTLServiceTicket *), @encode(BOOL), @encode(NSError *), 0); + + SEL finishedSel = @selector(objectFetcher:finishedWithData:error:); + + ticket.postedObject = bodyObject; + + ticket.executingQuery = query; + if (ticket.originalQuery == nil) { + ticket.originalQuery = query; + } + + GTMHTTPFetcherService *fetcherService = self.fetcherService; + GTMHTTPFetcher *fetcher; + + GTLUploadParameters *uploadParams = query.uploadParameters; + if (uploadParams == nil) { + // Not uploading a file with this request + fetcher = [fetcherService fetcherWithRequest:request]; + } else { + fetcher = [self uploadFetcherWithRequest:request + fetcherService:fetcherService + params:uploadParams]; + } + + if (finishedSelector) { + // if we don't have a method name, default to the finished selector as + // a useful fetcher log comment + fetcher.comment = NSStringFromSelector(finishedSelector); + } + + // allow the user to specify static app-wide cookies for fetching + NSInteger cookieStorageMethod = [self cookieStorageMethod]; + if (cookieStorageMethod >= 0) { + fetcher.cookieStorageMethod = cookieStorageMethod; + } + + if (!mayAuthorize) { + fetcher.authorizer = nil; + } + + // copy the ticket's retry settings into the fetcher + fetcher.retryEnabled = ticket.retryEnabled; + fetcher.maxRetryInterval = ticket.maxRetryInterval; + + BOOL shouldExamineRetries; +#if NS_BLOCKS_AVAILABLE + shouldExamineRetries = (ticket.retrySelector != nil + || ticket.retryBlock != nil); +#else + shouldExamineRetries = (ticket.retrySelector != nil); +#endif + if (shouldExamineRetries) { + [fetcher setRetrySelector:@selector(objectFetcher:willRetry:forError:)]; + } + + // remember the object fetcher in the ticket + ticket.objectFetcher = fetcher; + + // add parameters used by the callbacks + + [fetcher setProperty:objectClass forKey:kFetcherObjectClassKey]; + + [fetcher setProperty:delegate forKey:kFetcherDelegateKey]; + + [fetcher setProperty:NSStringFromSelector(finishedSelector) + forKey:kFetcherFinishedSelectorKey]; + + [fetcher setProperty:ticket + forKey:kFetcherTicketKey]; + +#if NS_BLOCKS_AVAILABLE + // copy the completion handler block to the heap; this does nothing if the + // block is already on the heap + completionHandler = [[completionHandler copy] autorelease]; + [fetcher setProperty:completionHandler + forKey:kFetcherCompletionHandlerKey]; +#endif + + // set the upload data + fetcher.postData = dataToPost; + + // failed fetches call the failure selector, which will delete the ticket + BOOL didFetch = [fetcher beginFetchWithDelegate:self + didFinishSelector:finishedSel]; + + // If something weird happens and the networking callbacks have been called + // already synchronously, we don't want to return the ticket since the caller + // will never know when to stop retaining it, so we'll make sure the + // success/failure callbacks have not yet been called by checking the + // ticket + if (!didFetch || ticket.hasCalledCallback) { + fetcher.properties = nil; + return nil; + } + + return ticket; +} + +- (GTMHTTPUploadFetcher *)uploadFetcherWithRequest:(NSURLRequest *)request + fetcherService:(GTMHTTPFetcherService *)fetcherService + params:(GTLUploadParameters *)uploadParams { + // Hang on to the user's requested chunk size, and ensure it's not tiny + NSUInteger uploadChunkSize = [self serviceUploadChunkSize]; + if (uploadChunkSize < kMinimumUploadChunkSize) { + uploadChunkSize = kMinimumUploadChunkSize; + } + +#ifdef GTL_TARGET_NAMESPACE + // Prepend the class name prefix + Class uploadClass = NSClassFromString(@GTL_TARGET_NAMESPACE_STRING + "_GTMHTTPUploadFetcher"); +#else + Class uploadClass = NSClassFromString(@"GTMHTTPUploadFetcher"); +#endif + GTL_ASSERT(uploadClass != nil, @"GTMHTTPUploadFetcher needed"); + + NSString *uploadMIMEType = uploadParams.MIMEType; + NSData *uploadData = uploadParams.data; + NSFileHandle *uploadFileHandle = uploadParams.fileHandle; + NSURL *uploadLocationURL = uploadParams.uploadLocationURL; + + GTMHTTPUploadFetcher *fetcher; + if (uploadData) { + fetcher = [uploadClass uploadFetcherWithRequest:request + uploadData:uploadData + uploadMIMEType:uploadMIMEType + chunkSize:uploadChunkSize + fetcherService:fetcherService]; + } else if (uploadLocationURL) { + GTL_DEBUG_ASSERT(uploadFileHandle != nil, + @"Resume requires a file handle"); + fetcher = [uploadClass uploadFetcherWithLocation:uploadLocationURL + uploadFileHandle:uploadFileHandle + uploadMIMEType:uploadMIMEType + chunkSize:uploadChunkSize + fetcherService:fetcherService]; + } else { + fetcher = [uploadClass uploadFetcherWithRequest:request + uploadFileHandle:uploadFileHandle + uploadMIMEType:uploadMIMEType + chunkSize:uploadChunkSize + fetcherService:fetcherService]; + } + + NSString *slug = [uploadParams slug]; + if ([slug length] > 0) { + [[fetcher mutableRequest] setValue:slug forHTTPHeaderField:@"Slug"]; + } + return fetcher; +} + +#pragma mark - + +// RPC fetch methods + +- (NSDictionary *)rpcPayloadForMethodNamed:(NSString *)methodName + parameters:(NSDictionary *)parameters + bodyObject:(GTLObject *)bodyObject + requestID:(NSString *)requestID { + GTL_DEBUG_ASSERT([requestID length] > 0, @"Got an empty request id"); + + // First, merge the developer key and bodyObject into the parameters. + + NSString *apiKey = self.APIKey; + NSUInteger apiKeyLen = [apiKey length]; + + NSString *const kDeveloperAPIParamKey = @"key"; + NSString *const kBodyObjectParamKey = @"resource"; + + NSDictionary *finalParams; + if ((apiKeyLen == 0) && (bodyObject == nil)) { + // Nothing needs to be added, just send the dict along. + finalParams = parameters; + } else { + NSMutableDictionary *worker = [NSMutableDictionary dictionary]; + if ([parameters count] > 0) { + [worker addEntriesFromDictionary:parameters]; + } + if ((apiKeyLen > 0) + && ([worker objectForKey:kDeveloperAPIParamKey] == nil)) { + [worker setObject:apiKey forKey:kDeveloperAPIParamKey]; + } + if (bodyObject != nil) { + GTL_DEBUG_ASSERT([parameters objectForKey:kBodyObjectParamKey] == nil, + @"There was already something under the 'data' key?!"); + NSMutableDictionary *json = [bodyObject JSON]; + if (json != nil) { + [worker setObject:json forKey:kBodyObjectParamKey]; + } + } + finalParams = worker; + } + + // Now, build up the full dictionary for the JSON-RPC (this is the body of + // the HTTP PUT). + + // Spec calls for the jsonrpc entry. Google doesn't require it, but include + // it so the code can work with other servers. + NSMutableDictionary *rpcPayload = [NSMutableDictionary dictionaryWithObjectsAndKeys: + @"2.0", @"jsonrpc", + methodName, @"method", + requestID, @"id", + nil]; + + // Google extension, provide the version of the api. + NSString *apiVersion = self.apiVersion; + if ([apiVersion length] > 0) { + [rpcPayload setObject:apiVersion forKey:@"apiVersion"]; + } + + if ([finalParams count] > 0) { + [rpcPayload setObject:finalParams forKey:@"params"]; + } + + return rpcPayload; +} + +- (GTLServiceTicket *)fetchObjectWithMethodNamed:(NSString *)methodName + objectClass:(Class)objectClass + parameters:(NSDictionary *)parameters + bodyObject:(GTLObject *)bodyObject + requestID:(NSString *)requestID + urlQueryParameters:(NSDictionary *)urlQueryParameters + delegate:(id)delegate + didFinishSelector:(SEL)finishedSelector + completionHandler:(id)completionHandler // GTLServiceCompletionHandler + executingQuery:(id)executingQuery + ticket:(GTLServiceTicket *)ticket { + GTL_DEBUG_ASSERT([methodName length] > 0, @"Got an empty method name"); + if ([methodName length] == 0) return nil; + + // If we didn't get a requestID, assign one (call came from one of the public + // calls that doesn't take a GTLQuery object). + if (requestID == nil) { + requestID = [GTLQuery nextRequestID]; + } + + NSData *dataToPost = nil; + GTLUploadParameters *uploadParameters = executingQuery.uploadParameters; + BOOL shouldSendBody = !uploadParameters.shouldSendUploadOnly; + if (shouldSendBody) { + NSDictionary *rpcPayload = [self rpcPayloadForMethodNamed:methodName + parameters:parameters + bodyObject:bodyObject + requestID:requestID]; + + NSError *error = nil; + dataToPost = [GTLJSONParser dataWithObject:rpcPayload + humanReadable:NO + error:&error]; + if (dataToPost == nil) { + // There is the chance something went into parameters that wasn't valid. + GTL_DEBUG_LOG(@"JSON generation error: %@", error); + return nil; + } + } + + BOOL isUploading = (uploadParameters != nil); + NSURL *rpcURL = (isUploading ? self.rpcUploadURL : self.rpcURL); + + if ([urlQueryParameters count] > 0) { + rpcURL = [GTLUtilities URLWithString:[rpcURL absoluteString] + queryParameters:urlQueryParameters]; + } + + BOOL mayAuthorize = (executingQuery ? + !executingQuery.shouldSkipAuthorization : YES); + + GTLServiceTicket *resultTicket = [self fetchObjectWithURL:rpcURL + objectClass:objectClass + bodyObject:bodyObject + dataToPost:dataToPost + ETag:nil + httpMethod:@"POST" + mayAuthorize:mayAuthorize + isREST:NO + delegate:delegate + didFinishSelector:finishedSelector + completionHandler:completionHandler + executingQuery:executingQuery + ticket:ticket]; + + // Set the fetcher log comment to default to the method name + NSUInteger pageNumber = resultTicket.pagesFetchedCounter; + if (pageNumber == 0) { + resultTicket.objectFetcher.comment = methodName; + } else { + // Also put the page number in the log comment + [resultTicket.objectFetcher setCommentWithFormat:@"%@ (page %lu)", + methodName, (unsigned long) (pageNumber + 1)]; + } + + return resultTicket; +} + +- (GTLServiceTicket *)executeBatchQuery:(GTLBatchQuery *)batch + delegate:(id)delegate + didFinishSelector:(SEL)finishedSelector + completionHandler:(id)completionHandler // GTLServiceCompletionHandler + ticket:(GTLServiceTicket *)ticket { + GTLBatchQuery *batchCopy = [[batch copy] autorelease]; + NSArray *queries = batchCopy.queries; + NSUInteger numberOfQueries = [queries count]; + if (numberOfQueries == 0) return nil; + + // Build up the array of RPC calls. + NSMutableArray *rpcPayloads = [NSMutableArray arrayWithCapacity:numberOfQueries]; + NSMutableArray *requestIDs = [NSMutableSet setWithCapacity:numberOfQueries]; + for (GTLQuery *query in queries) { + NSString *methodName = query.methodName; + NSDictionary *parameters = query.JSON; + GTLObject *bodyObject = query.bodyObject; + NSString *requestID = query.requestID; + + if ([methodName length] == 0 || [requestID length] == 0) { + GTL_DEBUG_ASSERT(0, @"Invalid query - id:%@ method:%@", + requestID, methodName); + return nil; + } + + GTL_DEBUG_ASSERT(query.additionalHTTPHeaders == nil, + @"additionalHTTPHeaders 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); + + NSDictionary *rpcPayload = [self rpcPayloadForMethodNamed:methodName + parameters:parameters + bodyObject:bodyObject + requestID:requestID]; + [rpcPayloads addObject:rpcPayload]; + + if ([requestIDs containsObject:requestID]) { + GTL_DEBUG_LOG(@"Duplicate request id in batch: %@", requestID); + return nil; + } + [requestIDs addObject:requestID]; + } + + NSError *error = nil; + NSData *dataToPost = nil; + dataToPost = [GTLJSONParser dataWithObject:rpcPayloads + humanReadable:NO + error:&error]; + if (dataToPost == nil) { + // There is the chance something went into parameters that wasn't valid. + GTL_DEBUG_LOG(@"JSON generation error: %@", error); + return nil; + } + + 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; + GTLServiceTicket *resultTicket = [self fetchObjectWithURL:rpcURL + objectClass:[GTLBatchResult class] + bodyObject:nil + dataToPost:dataToPost + ETag:nil + httpMethod:@"POST" + mayAuthorize:mayAuthorize + isREST:NO + delegate:delegate + didFinishSelector:finishedSelector + completionHandler:completionHandler + executingQuery:batch + ticket:ticket]; + +#if !STRIP_GTM_FETCH_LOGGING + // Set the fetcher log comment + // + // Because this has expensive set operations, it's conditionally + // compiled in + NSArray *methodNames = [queries valueForKey:@"methodName"]; + methodNames = [[NSSet setWithArray:methodNames] allObjects]; // de-dupe + NSString *methodsStr = [methodNames componentsJoinedByString:@", "]; + + NSUInteger pageNumber = ticket.pagesFetchedCounter; + NSString *pageStr = @""; + if (pageNumber > 0) { + pageStr = [NSString stringWithFormat:@"page %lu, ", + (unsigned long) pageNumber + 1]; + } + [resultTicket.objectFetcher setCommentWithFormat:@"batch: %@ (%@%lu queries)", + methodsStr, pageStr, (unsigned long) numberOfQueries]; +#endif + + return resultTicket; +} + + +#pragma mark - + +// REST fetch methods + +- (GTLServiceTicket *)fetchObjectWithURL:(NSURL *)targetURL + objectClass:(Class)objectClass + bodyObject:(GTLObject *)bodyObject + ETag:(NSString *)etag + httpMethod:(NSString *)httpMethod + mayAuthorize:(BOOL)mayAuthorize + delegate:(id)delegate + didFinishSelector:(SEL)finishedSelector + completionHandler:(id)completionHandler // GTLServiceCompletionHandler + ticket:(GTLServiceTicket *)ticket { + // if no URL was supplied, treat this as if the fetch failed (below) + // and immediately return a nil ticket, skipping the callbacks + // + // this might be considered normal (say, updating a read-only entry + // that lacks an edit link) though higher-level calls may assert or + // return errors depending on the specific usage + if (targetURL == nil) return nil; + + NSData *dataToPost = nil; + if (bodyObject != nil) { + NSError *error = nil; + + NSDictionary *whatToSend; + NSDictionary *json = bodyObject.JSON; + if (isRESTDataWrapperRequired_) { + // create the top-level "data" object + NSDictionary *dataDict = [NSDictionary dictionaryWithObject:json + forKey:@"data"]; + whatToSend = dataDict; + } else { + whatToSend = json; + } + dataToPost = [GTLJSONParser dataWithObject:whatToSend + humanReadable:NO + error:&error]; + if (dataToPost == nil) { + GTL_DEBUG_LOG(@"JSON generation error: %@", error); + } + } + + return [self fetchObjectWithURL:targetURL + objectClass:objectClass + bodyObject:bodyObject + dataToPost:dataToPost + ETag:etag + httpMethod:httpMethod + mayAuthorize:mayAuthorize + isREST:YES + delegate:delegate + didFinishSelector:finishedSelector + completionHandler:completionHandler + executingQuery:nil + ticket:ticket]; +} + +- (void)invokeProgressCallbackForTicket:(GTLServiceTicket *)ticket + deliveredBytes:(unsigned long long)numReadSoFar + totalBytes:(unsigned long long)total { + + SEL progressSelector = [ticket uploadProgressSelector]; + if (progressSelector) { + + GTMHTTPFetcher *fetcher = ticket.objectFetcher; + id delegate = [fetcher propertyForKey:kFetcherDelegateKey]; + + NSMethodSignature *signature = [delegate methodSignatureForSelector:progressSelector]; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; + + [invocation setSelector:progressSelector]; + [invocation setTarget:delegate]; + [invocation setArgument:&ticket atIndex:2]; + [invocation setArgument:&numReadSoFar atIndex:3]; + [invocation setArgument:&total atIndex:4]; + [invocation invoke]; + } + +#if NS_BLOCKS_AVAILABLE + GTLServiceUploadProgressBlock block = ticket.uploadProgressBlock; + if (block) { + block(ticket, numReadSoFar, total); + } +#endif +} + +// sentData callback from fetcher +- (void)objectFetcher:(GTMHTTPFetcher *)fetcher + didSendBytes:(NSInteger)bytesSent + totalBytesSent:(NSInteger)totalBytesSent +totalBytesExpectedToSend:(NSInteger)totalBytesExpected { + + GTLServiceTicket *ticket = [fetcher propertyForKey:kFetcherTicketKey]; + + [self invokeProgressCallbackForTicket:ticket + deliveredBytes:(unsigned long long)totalBytesSent + totalBytes:(unsigned long long)totalBytesExpected]; +} + +- (void)objectFetcher:(GTMHTTPFetcher *)fetcher finishedWithData:(NSData *)data error:(NSError *)error { + // we now have the JSON data for an object, or an error + if (error == nil) { + if ([data length] > 0) { + [self prepareToParseObjectForFetcher:fetcher]; + } else { + // no data (such as when deleting) + [self handleParsedObjectForFetcher:fetcher]; + } + } else { + // There was an error from the fetch + NSInteger status = [error code]; + if (status >= 300) { + // Return the HTTP error status code along with a more descriptive error + // from within the HTTP response payload. + NSData *responseData = fetcher.downloadedData; + if ([responseData length] > 0) { + NSDictionary *responseHeaders = fetcher.responseHeaders; + NSString *contentType = [responseHeaders objectForKey:@"Content-Type"]; + + if ([data length] > 0) { + if ([contentType hasPrefix:@"application/json"]) { + NSError *parseError = nil; + NSMutableDictionary *jsonWrapper = [GTLJSONParser objectWithData:data + error:&parseError]; + if (parseError) { + // We could not parse the JSON payload + error = parseError; + } else { + // Convert the JSON error payload into a structured error + NSMutableDictionary *errorJSON = [jsonWrapper valueForKey:@"error"]; + GTLErrorObject *errorObject = [GTLErrorObject objectWithJSON:errorJSON]; + error = [errorObject foundationError]; + } + } else { + // No structured JSON error was available; make a plaintext server + // error response visible in the error object. + NSString *reasonStr = [[[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding] autorelease]; + NSDictionary *userInfo = [NSDictionary dictionaryWithObject:reasonStr + forKey:NSLocalizedFailureReasonErrorKey]; + error = [NSError errorWithDomain:kGTMHTTPFetcherStatusDomain + code:status + userInfo:userInfo]; + } + } else { + // Response data length is zero; we'll settle for returning the + // fetcher's error. + } + } + } + + // store the error, call the callbacks, and bail + [fetcher setProperty:error + forKey:kFetcherFetchErrorKey]; + + [self handleParsedObjectForFetcher:fetcher]; + } +} + +// Three methods handle parsing of the fetched JSON data: +// - prepareToParse posts a start notification and then spawns off parsing +// on the operation queue (if there's an operation queue) +// - parseObject does the parsing of the JSON string +// - handleParsedObject posts the stop notification and calls the callback +// with the parsed object or an error +// +// The middle method may run on a separate thread. + +- (void)prepareToParseObjectForFetcher:(GTMHTTPFetcher *)fetcher { + // save the current thread into the fetcher, since we'll handle additional + // fetches and callbacks on this thread + [fetcher setProperty:[NSThread currentThread] + forKey:kFetcherCallbackThreadKey]; + + // copy the run loop modes, if any, so we don't need to access them + // from the parsing thread + [fetcher setProperty:[[[self runLoopModes] copy] autorelease] + forKey:kFetcherCallbackRunLoopModesKey]; + + // we post parsing notifications now to ensure they're on caller's + // original thread + GTLServiceTicket *ticket = [fetcher propertyForKey:kFetcherTicketKey]; + NSNotificationCenter *defaultNC = [NSNotificationCenter defaultCenter]; + [defaultNC postNotificationName:kGTLServiceTicketParsingStartedNotification + object:ticket]; + [fetcher setProperty:@"1" + forKey:kFetcherParsingNotificationKey]; + + id executingQuery = ticket.executingQuery; + if ([executingQuery isBatchQuery]) { + // build a dictionary of expected classes for the batch responses + GTLBatchQuery *batchQuery = executingQuery; + NSArray *queries = batchQuery.queries; + NSDictionary *batchClassMap = [NSMutableDictionary dictionaryWithCapacity:[queries count]]; + for (GTLQuery *query in queries) { + [batchClassMap setValue:query.expectedObjectClass + forKey:query.requestID]; + } + [fetcher setProperty:batchClassMap + forKey:kFetcherBatchClassMapKey]; + } + + // if there's an operation queue, then use that to schedule parsing on another + // thread + SEL parseSel = @selector(parseObjectFromDataOfFetcher:); + NSOperationQueue *queue = self.parseQueue; + if (queue) { + NSInvocationOperation *op; + op = [[[NSInvocationOperation alloc] initWithTarget:self + selector:parseSel + object:fetcher] autorelease]; + ticket.parseOperation = op; + [queue addOperation:op]; + // the fetcher now belongs to the parsing thread + } else { + // parse on the current thread, on Mac OS X 10.4 through 10.5.7 + // or when the project defines GTL_SKIP_PARSE_THREADING + [self performSelector:parseSel + withObject:fetcher]; + } +} + +- (void)parseObjectFromDataOfFetcher:(GTMHTTPFetcher *)fetcher { + // This method runs in a separate thread + + // Generally protect the fetcher properties, since canceling a ticket would + // release the fetcher properties dictionary + NSMutableDictionary *properties = [[fetcher.properties retain] autorelease]; + + // The callback thread is retaining the fetcher, so the fetcher shouldn't keep + // retaining the callback thread + NSThread *callbackThread = [properties valueForKey:kFetcherCallbackThreadKey]; + [[callbackThread retain] autorelease]; + [properties removeObjectForKey:kFetcherCallbackThreadKey]; + + GTLServiceTicket *ticket = [properties valueForKey:kFetcherTicketKey]; + [[ticket retain] autorelease]; + + NSDictionary *responseHeaders = fetcher.responseHeaders; + NSString *contentType = [responseHeaders objectForKey:@"Content-Type"]; + NSData *data = fetcher.downloadedData; + + NSOperation *parseOperation = ticket.parseOperation; + + GTL_DEBUG_ASSERT([contentType hasPrefix:@"application/json"], + @"Got unexpected content type '%@'", contentType); + if ([contentType hasPrefix:@"application/json"] && [data length] > 0) { +#if GTL_LOG_PERFORMANCE + NSTimeInterval secs1, secs2; + secs1 = [NSDate timeIntervalSinceReferenceDate]; +#endif + + NSError *parseError = nil; + NSMutableDictionary *jsonWrapper = [GTLJSONParser objectWithData:data + error:&parseError]; + if ([parseOperation isCancelled]) return; + + if (parseError != nil) { + [properties setValue:parseError forKey:kFetcherFetchErrorKey]; + } else { + NSMutableDictionary *json; + NSDictionary *batchClassMap = nil; + + // In theory, just checking for "application/json-rpc" vs + // "application/json" would work. But the JSON-RPC spec allows for + // "application/json" also so we have to carry a flag all the way in + // saying which type of result to expect and process as. + BOOL isREST = ticket.isREST; + if (isREST) { + if (isRESTDataWrapperRequired_) { + json = [jsonWrapper valueForKey:@"data"]; + } else { + json = jsonWrapper; + } + } else { + batchClassMap = [properties valueForKey:kFetcherBatchClassMapKey]; + if (batchClassMap) { + // A batch gets the whole array as it's json. + json = jsonWrapper; + } else { + json = [jsonWrapper valueForKey:@"result"]; + } + } + + if (json != nil) { + Class defaultClass = [properties valueForKey:kFetcherObjectClassKey]; + NSDictionary *surrogates = ticket.surrogates; + + GTLObject *parsedObject = [GTLObject objectForJSON:json + defaultClass:defaultClass + surrogates:surrogates + batchClassMap:batchClassMap]; + + [properties setValue:parsedObject forKey:kFetcherParsedObjectKey]; + } else if (!isREST) { + NSMutableDictionary *errorJSON = [jsonWrapper valueForKey:@"error"]; + GTL_DEBUG_ASSERT(errorJSON != nil, @"no result or error in response:\n%@", + jsonWrapper); + GTLErrorObject *errorObject = [GTLErrorObject objectWithJSON:errorJSON]; + NSError *error = [errorObject foundationError]; + + // Store the error and let it go to the callback + [properties setValue:error + forKey:kFetcherFetchErrorKey]; + } + } + +#if GTL_LOG_PERFORMANCE + secs2 = [NSDate timeIntervalSinceReferenceDate]; + NSLog(@"allocation of %@ took %f seconds", objectClass, secs2 - secs1); +#endif + } + + if ([parseOperation isCancelled]) return; + + SEL parseDoneSel = @selector(handleParsedObjectForFetcher:); + NSArray *runLoopModes = [properties valueForKey:kFetcherCallbackRunLoopModesKey]; + // If this callback was enqueued, then the fetcher has already released + // its delegateQueue. We'll use our own delegateQueue to determine how to + // invoke the callbacks. + NSOperationQueue *delegateQueue = self.delegateQueue; + if (delegateQueue) { + NSInvocationOperation *op; + op = [[[NSInvocationOperation alloc] initWithTarget:self + selector:parseDoneSel + object:fetcher] autorelease]; + [delegateQueue addOperation:op]; + } else if (runLoopModes) { + [self performSelector:parseDoneSel + onThread:callbackThread + withObject:fetcher + waitUntilDone:NO + modes:runLoopModes]; + } else { + // Defaults to common modes + [self performSelector:parseDoneSel + onThread:callbackThread + withObject:fetcher + waitUntilDone:NO]; + } + // the fetcher now belongs to the callback thread +} + +- (void)handleParsedObjectForFetcher:(GTMHTTPFetcher *)fetcher { + // After parsing is complete, this is invoked on the thread that the + // fetch was performed on + // + // There may not be an object due to a fetch or parsing error + + GTLServiceTicket *ticket = [fetcher propertyForKey:kFetcherTicketKey]; + ticket.parseOperation = nil; + + // unpack the callback parameters + id delegate = [fetcher propertyForKey:kFetcherDelegateKey]; + NSString *selString = [fetcher propertyForKey:kFetcherFinishedSelectorKey]; + SEL finishedSelector = NSSelectorFromString(selString); + +#if NS_BLOCKS_AVAILABLE + GTLServiceCompletionHandler completionHandler; + completionHandler = [fetcher propertyForKey:kFetcherCompletionHandlerKey]; +#else + id completionHandler = nil; +#endif + + GTLObject *object = [fetcher propertyForKey:kFetcherParsedObjectKey]; + NSError *error = [fetcher propertyForKey:kFetcherFetchErrorKey]; + + GTLQuery *executingQuery = (GTLQuery *)ticket.executingQuery; + + BOOL shouldFetchNextPages = ticket.shouldFetchNextPages; + GTLObject *previousObject = ticket.fetchedObject; + + if (shouldFetchNextPages + && (previousObject != nil) + && (object != nil)) { + // Accumulate new results + object = [self mergedNewResultObject:object + oldResultObject:previousObject + forQuery:executingQuery]; + } + + ticket.fetchedObject = object; + ticket.fetchError = error; + + if ([fetcher propertyForKey:kFetcherParsingNotificationKey] != nil) { + // we want to always balance the start and stop notifications + NSNotificationCenter *defaultNC = [NSNotificationCenter defaultCenter]; + [defaultNC postNotificationName:kGTLServiceTicketParsingStoppedNotification + object:ticket]; + } + + BOOL shouldCallCallbacks = YES; + + // Use the nextPageToken to fetch any later pages for non-batch queries + // + // This assumes a pagination model where objects have entries in an "items" + // field and a "nextPageToken" field, and queries support a "pageToken" + // parameter. + if (ticket.shouldFetchNextPages) { + // Determine if we should fetch more pages of results + + GTLQuery *nextPageQuery = [self nextPageQueryForQuery:executingQuery + result:object + ticket:ticket]; + if (nextPageQuery) { + BOOL isFetchingMore = [self fetchNextPageWithQuery:nextPageQuery + delegate:delegate + didFinishedSelector:finishedSelector + completionHandler:completionHandler + ticket:ticket]; + if (isFetchingMore) { + shouldCallCallbacks = NO; + } + } else { + // No more page tokens are present +#if DEBUG && !GTL_SKIP_PAGES_WARNING + // Each next page followed to accumulate all pages of a feed takes up to + // a few seconds. When multiple pages are being fetched, that + // usually indicates that a larger page size (that is, more items per + // feed fetched) should be requested. + // + // To avoid fetching many pages, set query.maxResults so the feed + // requested is large enough to rarely need to follow next links. + NSUInteger pageCount = ticket.pagesFetchedCounter; + if (pageCount > 2) { + NSString *queryLabel = [executingQuery isBatchQuery] ? + @"batch query" : executingQuery.methodName; + NSLog(@"Executing %@ required fetching %u pages; use a query with a" + @" larger maxResults for faster results", + queryLabel, (unsigned int) pageCount); + } +#endif + } + } + + // We no longer care about the queries for page 2 or later, so for the client + // inspecting the ticket in the callback, the executing query should be + // the original one + ticket.executingQuery = ticket.originalQuery; + + if (shouldCallCallbacks) { + // First, call query-specific callback blocks. We do this before the + // fetch callback to let applications do any final clean-up (or update + // their UI) in the fetch callback. + GTLQuery *originalQuery = (GTLQuery *)ticket.originalQuery; +#if NS_BLOCKS_AVAILABLE + if (![originalQuery isBatchQuery]) { + // Single query + GTLServiceCompletionHandler completionBlock = originalQuery.completionBlock; + if (completionBlock) { + completionBlock(ticket, object, error); + } + } else { + // Batch query + // + // We'll step through the queries of the original batch, not of the + // batch result + GTLBatchQuery *batchQuery = (GTLBatchQuery *)originalQuery; + GTLBatchResult *batchResult = (GTLBatchResult *)object; + NSDictionary *successes = batchResult.successes; + NSDictionary *failures = batchResult.failures; + + for (GTLQuery *oneQuery in batchQuery.queries) { + GTLServiceCompletionHandler completionBlock = oneQuery.completionBlock; + if (completionBlock) { + // If there was no networking error, look for a query-specific + // error or result + GTLObject *oneResult = nil; + NSError *oneError = error; + if (oneError == nil) { + NSString *requestID = [oneQuery requestID]; + GTLErrorObject *gtlError = [failures objectForKey:requestID]; + if (gtlError) { + oneError = [gtlError foundationError]; + } else { + oneResult = [successes objectForKey:requestID]; + if (oneResult == nil) { + // We found neither a success nor a failure for this + // query, unexpectedly + GTL_DEBUG_LOG(@"GTLService: Batch result missing for request %@", + requestID); + oneError = [NSError errorWithDomain:kGTLServiceErrorDomain + code:kGTLErrorQueryResultMissing + userInfo:nil]; + } + } + } + completionBlock(ticket, oneResult, oneError); + } + } + } +#endif + // Release query callback blocks + [originalQuery executionDidStop]; + + if (finishedSelector) { + [[self class] invokeCallback:finishedSelector + target:delegate + ticket:ticket + object:object + error:error]; + } + +#if NS_BLOCKS_AVAILABLE + if (completionHandler) { + completionHandler(ticket, object, error); + } +#endif + ticket.hasCalledCallback = YES; + } + fetcher.properties = nil; + +#if NS_BLOCKS_AVAILABLE + // Tickets don't know when the fetch has completed, so the service will + // release their blocks here to avoid unintended retain loops + ticket.retryBlock = nil; + ticket.uploadProgressBlock = nil; +#endif +} + +#pragma mark - + ++ (void)invokeCallback:(SEL)callbackSel + target:(id)target + ticket:(id)ticket + object:(id)object + error:(id)error { + + // GTL fetch callbacks have no return value + NSMethodSignature *signature = [target methodSignatureForSelector:callbackSel]; + NSInvocation *retryInvocation = [NSInvocation invocationWithMethodSignature:signature]; + [retryInvocation setSelector:callbackSel]; + [retryInvocation setTarget:target]; + [retryInvocation setArgument:&ticket atIndex:2]; + [retryInvocation setArgument:&object atIndex:3]; + [retryInvocation setArgument:&error atIndex:4]; + [retryInvocation invoke]; +} + +// The object fetcher may call into this retry method; this one invokes the +// selector provided by the user. +- (BOOL)objectFetcher:(GTMHTTPFetcher *)fetcher willRetry:(BOOL)willRetry forError:(NSError *)error { + + GTLServiceTicket *ticket = [fetcher propertyForKey:kFetcherTicketKey]; + SEL retrySelector = ticket.retrySelector; + if (retrySelector) { + id delegate = [fetcher propertyForKey:kFetcherDelegateKey]; + + willRetry = [self invokeRetrySelector:retrySelector + delegate:delegate + ticket:ticket + willRetry:willRetry + error:error]; + } + +#if NS_BLOCKS_AVAILABLE + BOOL (^retryBlock)(GTLServiceTicket *, BOOL, NSError *) = ticket.retryBlock; + if (retryBlock) { + willRetry = retryBlock(ticket, willRetry, error); + } +#endif + + return willRetry; +} + +- (BOOL)invokeRetrySelector:(SEL)retrySelector + delegate:(id)delegate + ticket:(GTLServiceTicket *)ticket + willRetry:(BOOL)willRetry + error:(NSError *)error { + + if ([delegate respondsToSelector:retrySelector]) { + // Unlike the retry selector invocation in GTMHTTPFetcher, this invocation + // passes the ticket rather than the fetcher as argument 2 + NSMethodSignature *signature = [delegate methodSignatureForSelector:retrySelector]; + NSInvocation *retryInvocation = [NSInvocation invocationWithMethodSignature:signature]; + [retryInvocation setSelector:retrySelector]; + [retryInvocation setTarget:delegate]; + [retryInvocation setArgument:&ticket atIndex:2]; // ticket passed + [retryInvocation setArgument:&willRetry atIndex:3]; + [retryInvocation setArgument:&error atIndex:4]; + [retryInvocation invoke]; + + [retryInvocation getReturnValue:&willRetry]; + } + return willRetry; +} + +- (BOOL)waitForTicket:(GTLServiceTicket *)ticket + timeout:(NSTimeInterval)timeoutInSeconds + fetchedObject:(GTLObject **)outObjectOrNil + error:(NSError **)outErrorOrNil { + + NSDate* giveUpDate = [NSDate dateWithTimeIntervalSinceNow:timeoutInSeconds]; + + // loop until the fetch completes with an object or an error, + // or until the timeout has expired + while (![ticket hasCalledCallback] + && [giveUpDate timeIntervalSinceNow] > 0) { + + // run the current run loop 1/1000 of a second to give the networking + // code a chance to work + NSDate *stopDate = [NSDate dateWithTimeIntervalSinceNow:0.001]; + [[NSRunLoop currentRunLoop] runUntilDate:stopDate]; + } + + NSError *fetchError = ticket.fetchError; + + if (![ticket hasCalledCallback] && fetchError == nil) { + fetchError = [NSError errorWithDomain:kGTLServiceErrorDomain + code:kGTLErrorWaitTimedOut + userInfo:nil]; + } + + if (outObjectOrNil) *outObjectOrNil = ticket.fetchedObject; + if (outErrorOrNil) *outErrorOrNil = fetchError; + + return (fetchError == nil); +} + +#pragma mark - + +// Given a single or batch query and its result, make a new query +// for the next pages, if any. Returns nil if there's no additional +// query to make. +// +// This method calls itself recursively to make the individual next page +// queries for a batch query. +- (id )nextPageQueryForQuery:(GTLQuery *)query + result:(GTLObject *)object + ticket:(GTLServiceTicket *)ticket { + if (!query.isBatchQuery) { + // This is a single query + + // Determine if we should fetch more pages of results + GTLQuery *nextPageQuery = nil; + NSString *nextPageToken = nil; + NSNumber *nextStartIndex = nil; + + if ([object respondsToSelector:@selector(nextPageToken)] + && [query respondsToSelector:@selector(pageToken)]) { + nextPageToken = [object performSelector:@selector(nextPageToken)]; + } + + if ([object respondsToSelector:@selector(nextStartIndex)] + && [query respondsToSelector:@selector(startIndex)]) { + nextStartIndex = [object performSelector:@selector(nextStartIndex)]; + } + + if (nextPageToken || nextStartIndex) { + // Make a query for the next page, preserving the request ID + nextPageQuery = [[query copy] autorelease]; + nextPageQuery.requestID = query.requestID; + + if (nextPageToken) { + [nextPageQuery performSelector:@selector(setPageToken:) + withObject:nextPageToken]; + } else { + // Use KVC to unwrap the scalar type instead of converting the + // NSNumber to an integer and using NSInvocation + [nextPageQuery setValue:nextStartIndex + forKey:@"startIndex"]; + } + } + return nextPageQuery; + } else { + // This is a batch query + // + // Check if there's a next page to fetch for any of the success + // results by invoking this method recursively on each of those results + GTLBatchResult *batchResult = (GTLBatchResult *)object; + GTLBatchQuery *nextPageBatchQuery = nil; + NSDictionary *successes = batchResult.successes; + + for (NSString *requestID in successes) { + GTLObject *singleObject = [successes objectForKey:requestID]; + GTLQuery *singleQuery = [ticket queryForRequestID:requestID]; + + GTLQuery *newQuery = [self nextPageQueryForQuery:singleQuery + result:singleObject + ticket:ticket]; + if (newQuery) { + // There is another query to fetch + if (nextPageBatchQuery == nil) { + nextPageBatchQuery = [GTLBatchQuery batchQuery]; + } + [nextPageBatchQuery addQuery:newQuery]; + } + } + return nextPageBatchQuery; + } +} + +// When a ticket is set to fetch more pages for feeds, this routine +// initiates the fetch for each additional feed page +- (BOOL)fetchNextPageWithQuery:(GTLQuery *)query + delegate:(id)delegate + didFinishedSelector:(SEL)finishedSelector + completionHandler:(GTLServiceCompletionHandler)completionHandler + ticket:(GTLServiceTicket *)ticket { + // Sanity check the number of pages fetched already + NSUInteger oldPagesFetchedCounter = ticket.pagesFetchedCounter; + + if (oldPagesFetchedCounter > kMaxNumberOfNextPagesFetched) { + // Sanity check failed: way too many pages were fetched + // + // The client should be querying with a higher max results per page + // to avoid this + GTL_DEBUG_ASSERT(0, @"Fetched too many next pages for %@", + query.methodName); + return NO; + } + + ticket.pagesFetchedCounter = 1 + oldPagesFetchedCounter; + + GTLServiceTicket *newTicket; + if (query.isBatchQuery) { + newTicket = [self executeBatchQuery:(GTLBatchQuery *)query + delegate:delegate + didFinishSelector:finishedSelector + completionHandler:completionHandler + ticket:ticket]; + } else { + newTicket = [self fetchObjectWithMethodNamed:query.methodName + objectClass:query.expectedObjectClass + parameters:query.JSON + bodyObject:query.bodyObject + requestID:query.requestID + urlQueryParameters:query.urlQueryParameters + delegate:delegate + didFinishSelector:finishedSelector + completionHandler:completionHandler + executingQuery:query + ticket:ticket]; + } + + // In the bizarre case that the fetch didn't begin, newTicket will be + // nil. So long as the new ticket is the same as the ticket we're + // continuing, then we're happy. + return (newTicket == ticket); +} + +// Given a new single or batch result (meaning additional pages for a previous +// query result), merge it into the old result. +- (GTLObject *)mergedNewResultObject:(GTLObject *)newResult + oldResultObject:(GTLObject *)oldResult + forQuery:(GTLQuery *)query { + if (query.isBatchQuery) { + // Batch query result + // + // The new batch results are a subset of the old result's queries, since + // not all queries in the batch necessarily have additional pages. + // + // New success objects replace old success objects, with the old items + // prepended; new failure objects replace old success objects. + // We will update the old batch results with accumulated items, using the + // new objects, and return the old batch. + // + // We reuse the old batch results object because it may include some earlier + // results which did not have additional pages. + GTLBatchResult *newBatchResult = (GTLBatchResult *)newResult; + GTLBatchResult *oldBatchResult = (GTLBatchResult *)oldResult; + + NSMutableDictionary *newSuccesses = newBatchResult.successes; + NSMutableDictionary *newFailures = newBatchResult.failures; + NSMutableDictionary *oldSuccesses = oldBatchResult.successes; + NSMutableDictionary *oldFailures = oldBatchResult.failures; + + for (NSString *requestID in newSuccesses) { + // Prepend the old items to the new response's items + // + // We can assume the objects are collections since they're present in + // additional pages. + GTLCollectionObject *newObj = [newSuccesses objectForKey:requestID]; + GTLCollectionObject *oldObj = [oldSuccesses objectForKey:requestID]; + + NSMutableArray *items = [NSMutableArray arrayWithArray:oldObj.items]; + [items addObjectsFromArray:newObj.items]; + [newObj performSelector:@selector(setItems:) withObject:items]; + + // Replace the old object with the new one + [oldSuccesses setObject:newObj forKey:requestID]; + } + + for (NSString *requestID in newFailures) { + // Replace old successes or failures with the new failure + GTLErrorObject *newError = [newFailures objectForKey:requestID]; + [oldFailures setObject:newError forKey:requestID]; + [oldSuccesses removeObjectForKey:requestID]; + } + return oldBatchResult; + } else { + // Single query result + // + // Merge the items into the new object, and return that. + // + // We can assume the objects are collections since they're present in + // additional pages. + GTLCollectionObject *newObj = (GTLCollectionObject *)newResult; + GTLCollectionObject *oldObj = (GTLCollectionObject *)oldResult; + + NSMutableArray *items = [NSMutableArray arrayWithArray:oldObj.items]; + [items addObjectsFromArray:newObj.items]; + [newObj performSelector:@selector(setItems:) withObject:items]; + + return newObj; + } +} + +#pragma mark - + +// GTLQuery methods. + +- (GTLServiceTicket *)executeQuery:(id)queryObj + delegate:(id)delegate + didFinishSelector:(SEL)finishedSelector { + if ([queryObj isBatchQuery]) { + return [self executeBatchQuery:queryObj + delegate:delegate + didFinishSelector:finishedSelector + completionHandler:NULL + ticket:nil]; + } + + GTLQuery *query = [[(GTLQuery *)queryObj copy] autorelease]; + NSString *methodName = query.methodName; + NSDictionary *params = query.JSON; + GTLObject *bodyObject = query.bodyObject; + + return [self fetchObjectWithMethodNamed:methodName + objectClass:query.expectedObjectClass + parameters:params + bodyObject:bodyObject + requestID:query.requestID + urlQueryParameters:query.urlQueryParameters + delegate:delegate + didFinishSelector:finishedSelector + completionHandler:nil + executingQuery:query + ticket:nil]; +} + +#if NS_BLOCKS_AVAILABLE +- (GTLServiceTicket *)executeQuery:(id)queryObj + completionHandler:(void (^)(GTLServiceTicket *ticket, id object, NSError *error))handler { + if ([queryObj isBatchQuery]) { + return [self executeBatchQuery:queryObj + delegate:nil + didFinishSelector:NULL + completionHandler:handler + ticket:nil]; + } + + GTLQuery *query = [[(GTLQuery *)queryObj copy] autorelease]; + NSString *methodName = query.methodName; + NSDictionary *params = query.JSON; + GTLObject *bodyObject = query.bodyObject; + + return [self fetchObjectWithMethodNamed:methodName + objectClass:query.expectedObjectClass + parameters:params + bodyObject:bodyObject + requestID:query.requestID + urlQueryParameters:query.urlQueryParameters + delegate:nil + didFinishSelector:NULL + completionHandler:handler + executingQuery:query + ticket:nil]; +} +#endif + +#pragma mark - + +- (GTLServiceTicket *)fetchObjectWithMethodNamed:(NSString *)methodName + parameters:(NSDictionary *)parameters + objectClass:(Class)objectClass + delegate:(id)delegate + didFinishSelector:(SEL)finishedSelector { + return [self fetchObjectWithMethodNamed:methodName + objectClass:objectClass + parameters:parameters + bodyObject:nil + requestID:nil + urlQueryParameters:nil + delegate:delegate + didFinishSelector:finishedSelector + completionHandler:nil + executingQuery:nil + ticket:nil]; +} + +- (GTLServiceTicket *)fetchObjectWithMethodNamed:(NSString *)methodName + insertingObject:(GTLObject *)bodyObject + objectClass:(Class)objectClass + delegate:(id)delegate + didFinishSelector:(SEL)finishedSelector { + return [self fetchObjectWithMethodNamed:methodName + objectClass:objectClass + parameters:nil + bodyObject:bodyObject + requestID:nil + urlQueryParameters:nil + delegate:delegate + didFinishSelector:finishedSelector + completionHandler:nil + executingQuery:nil + ticket:nil]; +} + +- (GTLServiceTicket *)fetchObjectWithMethodNamed:(NSString *)methodName + parameters:(NSDictionary *)parameters + insertingObject:(GTLObject *)bodyObject + objectClass:(Class)objectClass + delegate:(id)delegate + didFinishSelector:(SEL)finishedSelector { + return [self fetchObjectWithMethodNamed:methodName + objectClass:objectClass + parameters:parameters + bodyObject:bodyObject + requestID:nil + urlQueryParameters:nil + delegate:delegate + didFinishSelector:finishedSelector + completionHandler:nil + executingQuery:nil + ticket:nil]; +} + +#if NS_BLOCKS_AVAILABLE +- (GTLServiceTicket *)fetchObjectWithMethodNamed:(NSString *)methodName + parameters:(NSDictionary *)parameters + objectClass:(Class)objectClass + completionHandler:(void (^)(GTLServiceTicket *ticket, id object, NSError *error))handler { + return [self fetchObjectWithMethodNamed:methodName + objectClass:objectClass + parameters:parameters + bodyObject:nil + requestID:nil + urlQueryParameters:nil + delegate:nil + didFinishSelector:NULL + completionHandler:handler + executingQuery:nil + ticket:nil]; +} + +- (GTLServiceTicket *)fetchObjectWithMethodNamed:(NSString *)methodName + insertingObject:(GTLObject *)bodyObject + objectClass:(Class)objectClass + completionHandler:(void (^)(GTLServiceTicket *ticket, id object, NSError *error))handler { + return [self fetchObjectWithMethodNamed:methodName + objectClass:objectClass + parameters:nil + bodyObject:bodyObject + requestID:nil + urlQueryParameters:nil + delegate:nil + didFinishSelector:NULL + completionHandler:handler + executingQuery:nil + ticket:nil]; +} + +- (GTLServiceTicket *)fetchObjectWithMethodNamed:(NSString *)methodName + parameters:(NSDictionary *)parameters + insertingObject:(GTLObject *)bodyObject + objectClass:(Class)objectClass + completionHandler:(void (^)(GTLServiceTicket *ticket, id object, NSError *error))handler { + return [self fetchObjectWithMethodNamed:methodName + objectClass:objectClass + parameters:parameters + bodyObject:bodyObject + requestID:nil + urlQueryParameters:nil + delegate:nil + didFinishSelector:NULL + completionHandler:handler + executingQuery:nil + ticket:nil]; +} +#endif + +#pragma mark - + +// These external entry points doing a REST style fetch. + +- (GTLServiceTicket *)fetchObjectWithURL:(NSURL *)feedURL + delegate:(id)delegate + didFinishSelector:(SEL)finishedSelector { + // no object class specified; use registered class + return [self fetchObjectWithURL:feedURL + objectClass:nil + bodyObject:nil + ETag:nil + httpMethod:nil + mayAuthorize:YES + delegate:delegate + didFinishSelector:finishedSelector + completionHandler:nil + ticket:nil]; +} + +- (GTLServiceTicket *)fetchPublicObjectWithURL:(NSURL *)feedURL + objectClass:(Class)objectClass + delegate:(id)delegate + didFinishSelector:(SEL)finishedSelector { + return [self fetchObjectWithURL:feedURL + objectClass:objectClass + bodyObject:nil + ETag:nil + httpMethod:nil + mayAuthorize:NO + delegate:delegate + didFinishSelector:finishedSelector + completionHandler:nil + ticket:nil]; +} + +- (GTLServiceTicket *)fetchObjectWithURL:(NSURL *)feedURL + objectClass:(Class)objectClass + delegate:(id)delegate + didFinishSelector:(SEL)finishedSelector { + return [self fetchObjectWithURL:feedURL + objectClass:objectClass + bodyObject:nil + ETag:nil + httpMethod:nil + mayAuthorize:YES + delegate:delegate + didFinishSelector:finishedSelector + completionHandler:nil + ticket:nil]; +} + + +- (GTLServiceTicket *)fetchObjectByInsertingObject:(GTLObject *)bodyToPost + forURL:(NSURL *)destinationURL + delegate:(id)delegate + didFinishSelector:(SEL)finishedSelector { + Class objClass = [bodyToPost class]; + NSString *etag = ETagIfPresent(bodyToPost); + + return [self fetchObjectWithURL:destinationURL + objectClass:objClass + bodyObject:bodyToPost + ETag:etag + httpMethod:@"POST" + mayAuthorize:YES + delegate:delegate + didFinishSelector:finishedSelector + completionHandler:nil + ticket:nil]; +} + +- (GTLServiceTicket *)fetchObjectByUpdatingObject:(GTLObject *)bodyToPut + forURL:(NSURL *)destinationURL + delegate:(id)delegate + didFinishSelector:(SEL)finishedSelector { + Class objClass = [bodyToPut class]; + NSString *etag = ETagIfPresent(bodyToPut); + + return [self fetchObjectWithURL:destinationURL + objectClass:objClass + bodyObject:bodyToPut + ETag:etag + httpMethod:@"PUT" + mayAuthorize:YES + delegate:delegate + didFinishSelector:finishedSelector + completionHandler:nil + ticket:nil]; +} + + +- (GTLServiceTicket *)deleteResourceURL:(NSURL *)destinationURL + ETag:(NSString *)etagOrNil + delegate:(id)delegate + didFinishSelector:(SEL)finishedSelector { + return [self fetchObjectWithURL:destinationURL + objectClass:nil + bodyObject:nil + ETag:etagOrNil + httpMethod:@"DELETE" + mayAuthorize:YES + delegate:delegate + didFinishSelector:finishedSelector + completionHandler:nil + ticket:nil]; +} + + +#if NS_BLOCKS_AVAILABLE +- (GTLServiceTicket *)fetchObjectWithURL:(NSURL *)objectURL + completionHandler:(void (^)(GTLServiceTicket *ticket, id object, NSError *error))handler { + return [self fetchObjectWithURL:objectURL + objectClass:nil + bodyObject:nil + ETag:nil + httpMethod:nil + mayAuthorize:YES + delegate:nil + didFinishSelector:NULL + completionHandler:handler + ticket:nil]; +} + +- (GTLServiceTicket *)fetchObjectByInsertingObject:(GTLObject *)bodyToPost + forURL:(NSURL *)destinationURL + completionHandler:(void (^)(GTLServiceTicket *ticket, id object, NSError *error))handler { + Class objClass = [bodyToPost class]; + NSString *etag = ETagIfPresent(bodyToPost); + + return [self fetchObjectWithURL:destinationURL + objectClass:objClass + bodyObject:bodyToPost + ETag:etag + httpMethod:@"POST" + mayAuthorize:YES + delegate:nil + didFinishSelector:NULL + completionHandler:handler + ticket:nil]; +} + +- (GTLServiceTicket *)fetchObjectByUpdatingObject:(GTLObject *)bodyToPut + forURL:(NSURL *)destinationURL + completionHandler:(void (^)(GTLServiceTicket *ticket, id object, NSError *error))handler { + Class objClass = [bodyToPut class]; + NSString *etag = ETagIfPresent(bodyToPut); + + return [self fetchObjectWithURL:destinationURL + objectClass:objClass + bodyObject:bodyToPut + ETag:etag + httpMethod:@"PUT" + mayAuthorize:YES + delegate:nil + didFinishSelector:NULL + completionHandler:handler + ticket:nil]; +} + +- (GTLServiceTicket *)deleteResourceURL:(NSURL *)destinationURL + ETag:(NSString *)etagOrNil + completionHandler:(void (^)(GTLServiceTicket *ticket, id object, NSError *error))handler { + return [self fetchObjectWithURL:destinationURL + objectClass:nil + bodyObject:nil + ETag:etagOrNil + httpMethod:@"DELETE" + mayAuthorize:YES + delegate:nil + didFinishSelector:NULL + completionHandler:handler + ticket:nil]; +} + +#endif // NS_BLOCKS_AVAILABLE + +#pragma mark - + +- (NSString *)userAgent { + return userAgent_; +} + +- (void)setExactUserAgent:(NSString *)userAgent { + // internal use only + [userAgent_ release]; + userAgent_ = [userAgent copy]; +} + +- (void)setUserAgent:(NSString *)userAgent { + // remove whitespace and unfriendly characters + NSString *str = GTMCleanedUserAgentString(userAgent); + [self setExactUserAgent:str]; +} + +// +// The following methods pass through to the fetcher service object +// + +- (void)setCookieStorageMethod:(NSInteger)method { + self.fetcherService.cookieStorageMethod = method; +} + +- (NSInteger)cookieStorageMethod { + return self.fetcherService.cookieStorageMethod; +} + +- (void)setShouldFetchInBackground:(BOOL)flag { + self.fetcherService.shouldFetchInBackground = flag; +} + +- (BOOL)shouldFetchInBackground { + return self.fetcherService.shouldFetchInBackground; +} + +- (void)setDelegateQueue:(NSOperationQueue *)delegateQueue { + self.fetcherService.delegateQueue = delegateQueue; +} + +- (NSOperationQueue *)delegateQueue { + return self.fetcherService.delegateQueue; +} + +- (void)setRunLoopModes:(NSArray *)array { + self.fetcherService.runLoopModes = array; +} + +- (NSArray *)runLoopModes { + return self.fetcherService.runLoopModes; +} + +#pragma mark - + +// The service properties becomes the initial value for each future ticket's +// properties +- (void)setServiceProperties:(NSDictionary *)dict { + [serviceProperties_ autorelease]; + serviceProperties_ = [dict mutableCopy]; +} + +- (NSDictionary *)serviceProperties { + // be sure the returned pointer has the life of the autorelease pool, + // in case self is released immediately + return [[serviceProperties_ retain] autorelease]; +} + +- (void)setServiceProperty:(id)obj forKey:(NSString *)key { + + if (obj == nil) { + // user passed in nil, so delete the property + [serviceProperties_ removeObjectForKey:key]; + } else { + // be sure the property dictionary exists + if (serviceProperties_ == nil) { + [self setServiceProperties:[NSDictionary dictionary]]; + } + [serviceProperties_ setObject:obj forKey:key]; + } +} + +- (id)servicePropertyForKey:(NSString *)key { + id obj = [serviceProperties_ objectForKey:key]; + + // be sure the returned pointer has the life of the autorelease pool, + // in case self is released immediately + return [[obj retain] autorelease]; +} + +- (void)setServiceUserData:(id)userData { + [self setServiceProperty:userData forKey:kUserDataPropertyKey]; +} + +- (id)serviceUserData { + return [[[self servicePropertyForKey:kUserDataPropertyKey] retain] autorelease]; +} + +- (void)setAuthorizer:(id )authorizer { + self.fetcherService.authorizer = authorizer; +} + +- (id )authorizer { + return self.fetcherService.authorizer; +} + ++ (NSUInteger)defaultServiceUploadChunkSize { + // subclasses may override + return kStandardUploadChunkSize; +} + +- (NSUInteger)serviceUploadChunkSize { + return uploadChunkSize_; +} + +- (void)setServiceUploadChunkSize:(NSUInteger)val { + + if (val == kGTLStandardUploadChunkSize) { + // determine an appropriate upload chunk size for the system + + if (![GTMHTTPFetcher doesSupportSentDataCallback]) { + // for 10.4 and iPhone 2, we need a small upload chunk size so there + // are frequent intrachunk callbacks for progress monitoring + val = 75000; + } else { +#if GTL_IPHONE + val = 1000000; +#else + if (NSFoundationVersionNumber >= 751.00) { + // Mac OS X 10.6 + // + // we'll pick a huge upload chunk size, which minimizes http overhead + // and server effort, and we'll hope that NSURLConnection can finally + // handle big uploads reliably + val = 25000000; + } else { + // Mac OS X 10.5 + // + // NSURLConnection is more reliable on POSTs in 10.5 than it was in + // 10.4, but it still fails mysteriously on big uploads on some + // systems, so we'll limit the chunks to a megabyte + val = 1000000; + } +#endif + } + } + uploadChunkSize_ = val; +} + +@end + +@implementation GTLServiceTicket + +@synthesize shouldFetchNextPages = shouldFetchNextPages_, + surrogates = surrogates_, + uploadProgressSelector = uploadProgressSelector_, + retryEnabled = isRetryEnabled_, + hasCalledCallback = hasCalledCallback_, + retrySelector = retrySelector_, + maxRetryInterval = maxRetryInterval_, + objectFetcher = objectFetcher_, + postedObject = postedObject_, + fetchedObject = fetchedObject_, + executingQuery = executingQuery_, + originalQuery = originalQuery_, + fetchError = fetchError_, + pagesFetchedCounter = pagesFetchedCounter_, + APIKey = apiKey_, + parseOperation = parseOperation_, + isREST = isREST_; + +#if NS_BLOCKS_AVAILABLE +@synthesize retryBlock = retryBlock_; +#endif + ++ (id)ticketForService:(GTLService *)service { + return [[[self alloc] initWithService:service] autorelease]; +} + +- (id)initWithService:(GTLService *)service { + self = [super init]; + if (self) { + service_ = [service retain]; + + ticketProperties_ = [service.serviceProperties mutableCopy]; + surrogates_ = [service.surrogates retain]; + uploadProgressSelector_ = service.uploadProgressSelector; + isRetryEnabled_ = service.retryEnabled; + retrySelector_ = service.retrySelector; + maxRetryInterval_ = service.maxRetryInterval; + shouldFetchNextPages_ = service.shouldFetchNextPages; + apiKey_ = [service.APIKey copy]; + +#if NS_BLOCKS_AVAILABLE + uploadProgressBlock_ = [service.uploadProgressBlock copy]; + retryBlock_ = [service.retryBlock copy]; +#endif + } + return self; +} + +- (void)dealloc { + [service_ release]; + [ticketProperties_ release]; + [surrogates_ release]; + [objectFetcher_ release]; +#if NS_BLOCKS_AVAILABLE + [uploadProgressBlock_ release]; + [retryBlock_ release]; +#endif + [postedObject_ release]; + [fetchedObject_ release]; + [executingQuery_ release]; + [originalQuery_ release]; + [fetchError_ release]; + [apiKey_ release]; + [parseOperation_ release]; + + [super dealloc]; +} + +- (NSString *)description { + NSString *devKeyInfo = @""; + if (apiKey_ != nil) { + devKeyInfo = [NSString stringWithFormat:@" devKey:%@", apiKey_]; + } + + NSString *authorizerInfo = @""; + id authorizer = self.objectFetcher.authorizer; + if (authorizer != nil) { + authorizerInfo = [NSString stringWithFormat:@" authorizer:%@", authorizer]; + } + + return [NSString stringWithFormat:@"%@ %p: {service:%@%@%@ fetcher:%@ }", + [self class], self, service_, devKeyInfo, authorizerInfo, objectFetcher_]; +} + +- (void)pauseUpload { + BOOL canPause = [objectFetcher_ respondsToSelector:@selector(pauseFetching)]; + GTL_DEBUG_ASSERT(canPause, @"unpauseable ticket"); + + if (canPause) { + [(GTMHTTPUploadFetcher *)objectFetcher_ pauseFetching]; + } +} + +- (void)resumeUpload { + BOOL canResume = [objectFetcher_ respondsToSelector:@selector(resumeFetching)]; + GTL_DEBUG_ASSERT(canResume, @"unresumable ticket"); + + if (canResume) { + [(GTMHTTPUploadFetcher *)objectFetcher_ resumeFetching]; + } +} + +- (BOOL)isUploadPaused { + BOOL isPausable = [objectFetcher_ respondsToSelector:@selector(isPaused)]; + GTL_DEBUG_ASSERT(isPausable, @"unpauseable ticket"); + + if (isPausable) { + return [(GTMHTTPUploadFetcher *)objectFetcher_ isPaused]; + } + return NO; +} + +- (void)cancelTicket { + NSOperation *parseOperation = self.parseOperation; + [parseOperation cancel]; + self.parseOperation = nil; + + [objectFetcher_ stopFetching]; + objectFetcher_.properties = nil; + + self.objectFetcher = nil; + self.properties = nil; + self.uploadProgressSelector = nil; + +#if NS_BLOCKS_AVAILABLE + self.uploadProgressBlock = nil; + self.retryBlock = nil; +#endif + [self.executingQuery executionDidStop]; + self.executingQuery = self.originalQuery; + + [service_ autorelease]; + service_ = nil; +} + +- (id)service { + return service_; +} + +- (void)setUserData:(id)userData { + [self setProperty:userData forKey:kUserDataPropertyKey]; +} + +- (id)userData { + // be sure the returned pointer has the life of the autorelease pool, + // in case self is released immediately + return [[[self propertyForKey:kUserDataPropertyKey] retain] autorelease]; +} + +- (void)setProperties:(NSDictionary *)dict { + [ticketProperties_ autorelease]; + ticketProperties_ = [dict mutableCopy]; +} + +- (NSDictionary *)properties { + // be sure the returned pointer has the life of the autorelease pool, + // in case self is released immediately + return [[ticketProperties_ retain] autorelease]; +} + +- (void)setProperty:(id)obj forKey:(NSString *)key { + if (obj == nil) { + // user passed in nil, so delete the property + [ticketProperties_ removeObjectForKey:key]; + } else { + // be sure the property dictionary exists + if (ticketProperties_ == nil) { + // call setProperties so observers are notified + [self setProperties:[NSDictionary dictionary]]; + } + [ticketProperties_ setObject:obj forKey:key]; + } +} + +- (id)propertyForKey:(NSString *)key { + id obj = [ticketProperties_ objectForKey:key]; + + // be sure the returned pointer has the life of the autorelease pool, + // in case self is released immediately + return [[obj retain] autorelease]; +} + +- (NSDictionary *)surrogates { + return surrogates_; +} + +- (void)setSurrogates:(NSDictionary *)dict { + [surrogates_ autorelease]; + surrogates_ = [dict retain]; +} + +- (SEL)uploadProgressSelector { + return uploadProgressSelector_; +} + +- (void)setUploadProgressSelector:(SEL)progressSelector { + uploadProgressSelector_ = progressSelector; + + // if the user is turning on the progress selector in the ticket after the + // ticket's fetcher has been created, we need to give the fetcher our sentData + // callback. + // + // The progress monitor must be set in the service prior to creation of the + // ticket on 10.4 and iPhone 2.0, since on those systems the upload data must + // be wrapped with a ProgressMonitorInputStream prior to the creation of the + // fetcher. + if (progressSelector != NULL) { + SEL sentDataSel = @selector(objectFetcher:didSendBytes:totalBytesSent:totalBytesExpectedToSend:); + [[self objectFetcher] setSentDataSelector:sentDataSel]; + } +} + +#if NS_BLOCKS_AVAILABLE +- (void)setUploadProgressBlock:(GTLServiceUploadProgressBlock)block { + [uploadProgressBlock_ autorelease]; + uploadProgressBlock_ = [block copy]; + + if (uploadProgressBlock_) { + // As above, we need the fetcher to call us back when bytes are sent. + SEL sentDataSel = @selector(objectFetcher:didSendBytes:totalBytesSent:totalBytesExpectedToSend:); + [[self objectFetcher] setSentDataSelector:sentDataSel]; + } +} + +- (GTLServiceUploadProgressBlock)uploadProgressBlock { + return uploadProgressBlock_; +} +#endif + +- (NSInteger)statusCode { + return [objectFetcher_ statusCode]; +} + +- (GTLQuery *)queryForRequestID:(NSString *)requestID { + id queryObj = self.executingQuery; + if ([queryObj isBatchQuery]) { + GTLBatchQuery *batch = (GTLBatchQuery *)queryObj; + GTLQuery *result = [batch queryForRequestID:requestID]; + return result; + } else { + GTL_DEBUG_ASSERT(0, @"just use ticket.executingQuery"); + return nil; + } +} + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLTargetNamespace.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLTargetNamespace.h new file mode 100644 index 0000000000..9e08a9e426 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLTargetNamespace.h @@ -0,0 +1,58 @@ +// +// Makes the value of GTL_TARGET_NAMESPACE a prefix for all GTL +// library class names +// + +// +// To avoid global namespace issues, define GTL_TARGET_NAMESPACE to a short +// string in your target if you are using the GTL library in a shared-code +// environment like a plug-in. +// +// For example: -DGTL_TARGET_NAMESPACE=MyPlugin +// + +// +// com.google.GTLFramework v. 2.0 (29 classes) 2011-10-25 19:25:36 -0700 +// + +#if defined(__OBJC__) && defined(GTL_TARGET_NAMESPACE) + + #define _GTL_NS_SYMBOL_INNER(ns, symbol) ns ## _ ## symbol + #define _GTL_NS_SYMBOL_MIDDLE(ns, symbol) _GTL_NS_SYMBOL_INNER(ns, symbol) + #define _GTL_NS_SYMBOL(symbol) _GTL_NS_SYMBOL_MIDDLE(GTL_TARGET_NAMESPACE, symbol) + + #define _GTL_NS_STRING_INNER(ns) #ns + #define _GTL_NS_STRING_MIDDLE(ns) _GTL_NS_STRING_INNER(ns) + #define GTL_TARGET_NAMESPACE_STRING _GTL_NS_STRING_MIDDLE(GTL_TARGET_NAMESPACE) + + #define GTLBatchQuery _GTL_NS_SYMBOL(GTLBatchQuery) + #define GTLBatchResult _GTL_NS_SYMBOL(GTLBatchResult) + #define GTLCollectionObject _GTL_NS_SYMBOL(GTLCollectionObject) + #define GTLDateTime _GTL_NS_SYMBOL(GTLDateTime) + #define GTLErrorObject _GTL_NS_SYMBOL(GTLErrorObject) + #define GTLErrorObjectData _GTL_NS_SYMBOL(GTLErrorObjectData) + #define GTLJSONParser _GTL_NS_SYMBOL(GTLJSONParser) + #define GTLObject _GTL_NS_SYMBOL(GTLObject) + #define GTLQuery _GTL_NS_SYMBOL(GTLQuery) + #define GTLRuntimeCommon _GTL_NS_SYMBOL(GTLRuntimeCommon) + #define GTLService _GTL_NS_SYMBOL(GTLService) + #define GTLServiceTicket _GTL_NS_SYMBOL(GTLServiceTicket) + #define GTLUploadParameters _GTL_NS_SYMBOL(GTLUploadParameters) + #define GTLUtilities _GTL_NS_SYMBOL(GTLUtilities) + #define GTMCachedURLResponse _GTL_NS_SYMBOL(GTMCachedURLResponse) + #define GTMCookieStorage _GTL_NS_SYMBOL(GTMCookieStorage) + #define GTMGatherInputStream _GTL_NS_SYMBOL(GTMGatherInputStream) + #define GTMHTTPFetcher _GTL_NS_SYMBOL(GTMHTTPFetcher) + #define GTMHTTPFetcherService _GTL_NS_SYMBOL(GTMHTTPFetcherService) + #define GTMHTTPFetchHistory _GTL_NS_SYMBOL(GTMHTTPFetchHistory) + #define GTMHTTPUploadFetcher _GTL_NS_SYMBOL(GTMHTTPUploadFetcher) + #define GTMMIMEDocument _GTL_NS_SYMBOL(GTMMIMEDocument) + #define GTMMIMEPart _GTL_NS_SYMBOL(GTMMIMEPart) + #define GTMOAuth2Authentication _GTL_NS_SYMBOL(GTMOAuth2Authentication) + #define GTMOAuth2AuthorizationArgs _GTL_NS_SYMBOL(GTMOAuth2AuthorizationArgs) + #define GTMOAuth2SignIn _GTL_NS_SYMBOL(GTMOAuth2SignIn) + #define GTMOAuth2WindowController _GTL_NS_SYMBOL(GTMOAuth2WindowController) + #define GTMReadMonitorInputStream _GTL_NS_SYMBOL(GTMReadMonitorInputStream) + #define GTMURLCache _GTL_NS_SYMBOL(GTMURLCache) + +#endif diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLUploadParameters.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLUploadParameters.h new file mode 100644 index 0000000000..a3c1d9dc61 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLUploadParameters.h @@ -0,0 +1,60 @@ +/* Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLUploadParameters.h +// + +// Uploading documentation: +// https://code.google.com/p/google-api-objectivec-client/wiki/Introduction#Uploading_Files + +#import + +#import "GTLDefines.h" + +@interface GTLUploadParameters : NSObject { + @private + NSString *MIMEType_; + NSData *data_; + NSFileHandle *fileHandle_; + NSURL *uploadLocationURL_; + NSString *slug_; + BOOL shouldSendUploadOnly_; +} + +// Uploading requires MIME type and one of +// - data to be uploaded +// - file handle for uploading +@property (copy) NSString *MIMEType; +@property (retain) NSData *data; +@property (retain) NSFileHandle *fileHandle; + +// Resuming an in-progress upload is done with the upload location URL, +// and requires a file handle for uploading +@property (retain) NSURL *uploadLocationURL; + +// Some services need a slug (filename) header +@property (copy) NSString *slug; + +// Uploads may be done without a JSON body in the initial request +@property (assign) BOOL shouldSendUploadOnly; + ++ (GTLUploadParameters *)uploadParametersWithData:(NSData *)data + MIMEType:(NSString *)mimeType GTL_NONNULL((1,2)); + ++ (GTLUploadParameters *)uploadParametersWithFileHandle:(NSFileHandle *)fileHandle + MIMEType:(NSString *)mimeType GTL_NONNULL((1,2)); + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLUploadParameters.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLUploadParameters.m new file mode 100644 index 0000000000..1a668a9d67 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLUploadParameters.m @@ -0,0 +1,107 @@ +/* Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTLObject.m +// + +#include + +#import "GTLUploadParameters.h" + +@implementation GTLUploadParameters + +@synthesize MIMEType = MIMEType_, + data = data_, + fileHandle = fileHandle_, + uploadLocationURL = uploadLocationURL_, + slug = slug_, + shouldSendUploadOnly = shouldSendUploadOnly_; + ++ (GTLUploadParameters *)uploadParametersWithData:(NSData *)data + MIMEType:(NSString *)mimeType { + GTLUploadParameters *params = [[[GTLUploadParameters alloc] init] autorelease]; + params.data = data; + params.MIMEType = mimeType; + return params; +} + ++ (GTLUploadParameters *)uploadParametersWithFileHandle:(NSFileHandle *)fileHandle + MIMEType:(NSString *)mimeType { + GTLUploadParameters *params = [[[GTLUploadParameters alloc] init] autorelease]; + params.fileHandle = fileHandle; + params.MIMEType = mimeType; + return params; +} + +- (id)copyWithZone:(NSZone *)zone { + GTLUploadParameters *newParams = [[[self class] allocWithZone:zone] init]; + newParams.MIMEType = self.MIMEType; + newParams.data = self.data; + newParams.fileHandle = self.fileHandle; + newParams.uploadLocationURL = self.uploadLocationURL; + newParams.slug = self.slug; + newParams.shouldSendUploadOnly = self.shouldSendUploadOnly; + return newParams; +} + +- (void)dealloc { + [MIMEType_ release]; + [data_ release]; + [fileHandle_ release]; + [uploadLocationURL_ release]; + [slug_ release]; + + [super dealloc]; +} + +- (NSString *)description { + NSMutableArray *array = [NSMutableArray array]; + NSString *str = [NSString stringWithFormat:@"MIMEType:%@", MIMEType_]; + [array addObject:str]; + + if (data_) { + str = [NSString stringWithFormat:@"data:%llu bytes", + (unsigned long long)[data_ length]]; + [array addObject:str]; + } + + if (fileHandle_) { + str = [NSString stringWithFormat:@"fileHandle:%@", fileHandle_]; + [array addObject:str]; + } + + if (uploadLocationURL_) { + str = [NSString stringWithFormat:@"uploadLocation:%@", + [uploadLocationURL_ absoluteString]]; + [array addObject:str]; + } + + if (slug_) { + str = [NSString stringWithFormat:@"slug:%@", slug_]; + [array addObject:str]; + } + + if (shouldSendUploadOnly_) { + [array addObject:@"shouldSendUploadOnly"]; + } + + NSString *descStr = [array componentsJoinedByString:@", "]; + str = [NSString stringWithFormat:@"%@ %p: {%@}", + [self class], self, descStr]; + return str; +} + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLUtilities.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLUtilities.h new file mode 100644 index 0000000000..97f4bf0790 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLUtilities.h @@ -0,0 +1,93 @@ +/* Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +#ifndef SKIP_GTL_DEFINES + #import "GTLDefines.h" +#endif + +// helper functions for implementing isEqual: +BOOL GTL_AreEqualOrBothNil(id obj1, id obj2); +BOOL GTL_AreBoolsEqual(BOOL b1, BOOL b2); + +// Helper to ensure a number is a number. +// +// The GoogleAPI servers will send numbers >53 bits as strings to avoid +// bugs in some JavaScript implementations. Work around this by catching +// the string and turning it back into a number. +NSNumber *GTL_EnsureNSNumber(NSNumber *num); + +@interface GTLUtilities : NSObject + +// +// String encoding +// + +// URL encoding, different for parts of URLs and parts of URL parameters +// +// +stringByURLEncodingString just makes a string legal for a URL +// +// +stringByURLEncodingForURI also encodes some characters that are legal in +// URLs but should not be used in URIs, +// per http://bitworking.org/projects/atom/rfc5023.html#rfc.section.9.7 +// +// +stringByURLEncodingStringParameter is like +stringByURLEncodingForURI but +// replaces space characters with + characters rather than percent-escaping them +// ++ (NSString *)stringByURLEncodingString:(NSString *)str; ++ (NSString *)stringByURLEncodingForURI:(NSString *)str; ++ (NSString *)stringByURLEncodingStringParameter:(NSString *)str; + +// Percent-encoded UTF-8 ++ (NSString *)stringByPercentEncodingUTF8ForString:(NSString *)str; + +// Key-value coding searches in an array +// +// Utilities to get from an array objects having a known value (or nil) +// at a keyPath + ++ (NSArray *)objectsFromArray:(NSArray *)sourceArray + withValue:(id)desiredValue + forKeyPath:(NSString *)keyPath; + ++ (id)firstObjectFromArray:(NSArray *)sourceArray + withValue:(id)desiredValue + forKeyPath:(NSString *)keyPath; + +// +// Version helpers +// + ++ (NSComparisonResult)compareVersion:(NSString *)ver1 toVersion:(NSString *)ver2; + +// +// URL builder +// + +// If there are already query parameters on urlString, the new ones are simple +// appended after them. ++ (NSURL *)URLWithString:(NSString *)urlString + queryParameters:(NSDictionary *)queryParameters; + +// Allocate a global dictionary ++ (NSMutableDictionary *)newStaticDictionary; + +// Walk up the class tree merging dictionaries and return the result. ++ (NSDictionary *)mergedClassDictionaryForSelector:(SEL)selector + startClass:(Class)startClass + ancestorClass:(Class)ancestorClass + cache:(NSMutableDictionary *)cache; +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLUtilities.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLUtilities.m new file mode 100644 index 0000000000..90d8e74598 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTL/GTLUtilities.m @@ -0,0 +1,358 @@ +/* Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "GTLUtilities.h" + +#include + +@implementation GTLUtilities + +#pragma mark String encoding + +// URL Encoding + ++ (NSString *)stringByURLEncodingString:(NSString *)str { + NSString *result = [str stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + return result; +} + +// NSURL's stringByAddingPercentEscapesUsingEncoding: does not escape +// some characters that should be escaped in URL parameters, like / and ?; +// we'll use CFURL to force the encoding of those +// +// Reference: http://www.ietf.org/rfc/rfc3986.txt + +const CFStringRef kCharsToForceEscape = CFSTR("!*'();:@&=+$,/?%#[]"); + ++ (NSString *)stringByURLEncodingForURI:(NSString *)str { + + NSString *resultStr = str; + + CFStringRef originalString = (CFStringRef) str; + CFStringRef leaveUnescaped = NULL; + + CFStringRef escapedStr; + escapedStr = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, + originalString, + leaveUnescaped, + kCharsToForceEscape, + kCFStringEncodingUTF8); + if (escapedStr) { + resultStr = [(id)CFMakeCollectable(escapedStr) autorelease]; + } + return resultStr; +} + ++ (NSString *)stringByURLEncodingStringParameter:(NSString *)str { + // For parameters, we'll explicitly leave spaces unescaped now, and replace + // them with +'s + NSString *resultStr = str; + + CFStringRef originalString = (CFStringRef) str; + CFStringRef leaveUnescaped = CFSTR(" "); + + CFStringRef escapedStr; + escapedStr = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, + originalString, + leaveUnescaped, + kCharsToForceEscape, + kCFStringEncodingUTF8); + + if (escapedStr) { + NSMutableString *mutableStr = [NSMutableString stringWithString:(NSString *)escapedStr]; + CFRelease(escapedStr); + + // replace spaces with plusses + [mutableStr replaceOccurrencesOfString:@" " + withString:@"+" + options:0 + range:NSMakeRange(0, [mutableStr length])]; + resultStr = mutableStr; + } + return resultStr; +} + ++ (NSString *)stringByPercentEncodingUTF8ForString:(NSString *)inputStr { + + // Encode per http://bitworking.org/projects/atom/rfc5023.html#rfc.section.9.7 + // + // This is used for encoding upload slug headers + // + // Step through the string as UTF-8, and replace characters outside 20..7E + // (and the percent symbol itself, 25) with percent-encodings + // + // We avoid creating an encoding string unless we encounter some characters + // that require it + const char* utf8 = [inputStr UTF8String]; + if (utf8 == NULL) { + return nil; + } + + NSMutableString *encoded = nil; + + for (unsigned int idx = 0; utf8[idx] != '\0'; idx++) { + + unsigned char currChar = (unsigned char)utf8[idx]; + if (currChar < 0x20 || currChar == 0x25 || currChar > 0x7E) { + + if (encoded == nil) { + // Start encoding and catch up on the character skipped so far + encoded = [[[NSMutableString alloc] initWithBytes:utf8 + length:idx + encoding:NSUTF8StringEncoding] autorelease]; + } + + // append this byte as a % and then uppercase hex + [encoded appendFormat:@"%%%02X", currChar]; + + } else { + // This character does not need encoding + // + // Encoded is nil here unless we've encountered a previous character + // that needed encoding + [encoded appendFormat:@"%c", currChar]; + } + } + + if (encoded) { + return encoded; + } + + return inputStr; +} + +#pragma mark Key-Value Coding Searches in an Array + ++ (NSArray *)objectsFromArray:(NSArray *)sourceArray + withValue:(id)desiredValue + forKeyPath:(NSString *)keyPath { + // Step through all entries, get the value from + // the key path, and see if it's equal to the + // desired value + NSMutableArray *results = [NSMutableArray array]; + + for(id obj in sourceArray) { + id val = [obj valueForKeyPath:keyPath]; + if (GTL_AreEqualOrBothNil(val, desiredValue)) { + + // found a match; add it to the results array + [results addObject:obj]; + } + } + return results; +} + ++ (id)firstObjectFromArray:(NSArray *)sourceArray + withValue:(id)desiredValue + forKeyPath:(NSString *)keyPath { + for (id obj in sourceArray) { + id val = [obj valueForKeyPath:keyPath]; + if (GTL_AreEqualOrBothNil(val, desiredValue)) { + // found a match; return it + return obj; + } + } + return nil; +} + +#pragma mark Version helpers + +// compareVersion compares two strings in 1.2.3.4 format +// missing fields are interpreted as zeros, so 1.2 = 1.2.0.0 ++ (NSComparisonResult)compareVersion:(NSString *)ver1 toVersion:(NSString *)ver2 { + + static NSCharacterSet* dotSet = nil; + if (dotSet == nil) { + dotSet = [[NSCharacterSet characterSetWithCharactersInString:@"."] retain]; + } + + if (ver1 == nil) ver1 = @""; + if (ver2 == nil) ver2 = @""; + + NSScanner* scanner1 = [NSScanner scannerWithString:ver1]; + NSScanner* scanner2 = [NSScanner scannerWithString:ver2]; + + [scanner1 setCharactersToBeSkipped:dotSet]; + [scanner2 setCharactersToBeSkipped:dotSet]; + + int partA1 = 0, partA2 = 0, partB1 = 0, partB2 = 0; + int partC1 = 0, partC2 = 0, partD1 = 0, partD2 = 0; + + if ([scanner1 scanInt:&partA1] && [scanner1 scanInt:&partB1] + && [scanner1 scanInt:&partC1] && [scanner1 scanInt:&partD1]) { + } + if ([scanner2 scanInt:&partA2] && [scanner2 scanInt:&partB2] + && [scanner2 scanInt:&partC2] && [scanner2 scanInt:&partD2]) { + } + + if (partA1 != partA2) return ((partA1 < partA2) ? NSOrderedAscending : NSOrderedDescending); + if (partB1 != partB2) return ((partB1 < partB2) ? NSOrderedAscending : NSOrderedDescending); + if (partC1 != partC2) return ((partC1 < partC2) ? NSOrderedAscending : NSOrderedDescending); + if (partD1 != partD2) return ((partD1 < partD2) ? NSOrderedAscending : NSOrderedDescending); + return NSOrderedSame; +} + +#pragma mark URL builder + ++ (NSURL *)URLWithString:(NSString *)urlString + queryParameters:(NSDictionary *)queryParameters { + if ([urlString length] == 0) return nil; + + NSString *fullURLString; + if ([queryParameters count] > 0) { + NSMutableArray *queryItems = [NSMutableArray arrayWithCapacity:[queryParameters count]]; + + // sort the custom parameter keys so that we have deterministic parameter + // order for unit tests + NSArray *queryKeys = [queryParameters allKeys]; + NSArray *sortedQueryKeys = [queryKeys sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]; + + for (NSString *paramKey in sortedQueryKeys) { + NSString *paramValue = [queryParameters valueForKey:paramKey]; + + NSString *paramItem = [NSString stringWithFormat:@"%@=%@", + [self stringByURLEncodingStringParameter:paramKey], + [self stringByURLEncodingStringParameter:paramValue]]; + + [queryItems addObject:paramItem]; + } + + NSString *paramStr = [queryItems componentsJoinedByString:@"&"]; + + BOOL hasQMark = ([urlString rangeOfString:@"?"].location == NSNotFound); + char joiner = hasQMark ? '?' : '&'; + fullURLString = [NSString stringWithFormat:@"%@%c%@", + urlString, joiner, paramStr]; + } else { + fullURLString = urlString; + } + NSURL *result = [NSURL URLWithString:fullURLString]; + return result; +} + +#pragma mark Collections + ++ (NSMutableDictionary *)newStaticDictionary { + NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; + + // make the dictionary ineligible for garbage collection +#if !GTL_IPHONE + [[NSGarbageCollector defaultCollector] disableCollectorForPointer:dict]; +#endif + return dict; +} + ++ (NSDictionary *)mergedClassDictionaryForSelector:(SEL)selector + startClass:(Class)startClass + ancestorClass:(Class)ancestorClass + cache:(NSMutableDictionary *)cache { + NSDictionary *result; + @synchronized(cache) { + result = [cache objectForKey:startClass]; + if (result == nil) { + // Collect the class's dictionary. + NSDictionary *classDict = [startClass performSelector:selector]; + + // Collect the parent class's merged dictionary. + NSDictionary *parentClassMergedDict; + if ([startClass isEqual:ancestorClass]) { + parentClassMergedDict = nil; + } else { + Class parentClass = class_getSuperclass(startClass); + parentClassMergedDict = + [GTLUtilities mergedClassDictionaryForSelector:selector + startClass:parentClass + ancestorClass:ancestorClass + cache:cache]; + } + + // Merge this class's into the parent's so things properly override. + NSMutableDictionary *mergeDict; + if (parentClassMergedDict != nil) { + mergeDict = + [NSMutableDictionary dictionaryWithDictionary:parentClassMergedDict]; + } else { + mergeDict = [NSMutableDictionary dictionary]; + } + if (classDict != nil) { + [mergeDict addEntriesFromDictionary:classDict]; + } + + // Make an immutable version. + result = [NSDictionary dictionaryWithDictionary:mergeDict]; + + // Save it. + [cache setObject:result forKey:(id)startClass]; + } + } + return result; +} + +@end + +// isEqual: has the fatal flaw that it doesn't deal well with the receiver +// being nil. We'll use this utility instead. +BOOL GTL_AreEqualOrBothNil(id obj1, id obj2) { + if (obj1 == obj2) { + return YES; + } + if (obj1 && obj2) { + BOOL areEqual = [obj1 isEqual:obj2]; + return areEqual; + } + return NO; +} + +BOOL GTL_AreBoolsEqual(BOOL b1, BOOL b2) { + // avoid comparison problems with boolean types by negating + // both booleans + return (!b1 == !b2); +} + +NSNumber *GTL_EnsureNSNumber(NSNumber *num) { + // If the server returned a string object where we expect a number, try + // to make a number object. + if ([num isKindOfClass:[NSString class]]) { + NSNumber *newNum; + NSString *str = (NSString *)num; + if ([str rangeOfString:@"."].location != NSNotFound) { + // This is a floating-point number. + // Force the parser to use '.' as the decimal separator. + static NSLocale *usLocale = nil; + @synchronized([GTLUtilities class]) { + if (usLocale == nil) { + usLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]; + } + newNum = [NSDecimalNumber decimalNumberWithString:(NSString*)num + locale:(id)usLocale]; + } + } else { + // NSDecimalNumber +decimalNumberWithString:locale: + // does not correctly create an NSNumber for large values like + // 71100000000007780. + if ([str hasPrefix:@"-"]) { + newNum = [NSNumber numberWithLongLong:[str longLongValue]]; + } else { + const char *utf8 = [str UTF8String]; + unsigned long long ull = strtoull(utf8, NULL, 10); + newNum = [NSNumber numberWithUnsignedLongLong:ull]; + } + } + if (newNum) { + num = newNum; + } + } + return num; +} diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTMDefines.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTMDefines.h new file mode 100644 index 0000000000..c2958487fa --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTMDefines.h @@ -0,0 +1,441 @@ +// +// GTMDefines.h +// +// Copyright 2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +// ============================================================================ + +#include +#include + +#ifdef __OBJC__ +#include +#endif // __OBJC__ + +#if TARGET_OS_IPHONE +#include +#endif // TARGET_OS_IPHONE + +// Not all MAC_OS_X_VERSION_10_X macros defined in past SDKs +#ifndef MAC_OS_X_VERSION_10_5 + #define MAC_OS_X_VERSION_10_5 1050 +#endif +#ifndef MAC_OS_X_VERSION_10_6 + #define MAC_OS_X_VERSION_10_6 1060 +#endif +#ifndef MAC_OS_X_VERSION_10_7 + #define MAC_OS_X_VERSION_10_7 1070 +#endif + +// Not all __IPHONE_X macros defined in past SDKs +#ifndef __IPHONE_3_0 + #define __IPHONE_3_0 30000 +#endif +#ifndef __IPHONE_3_1 + #define __IPHONE_3_1 30100 +#endif +#ifndef __IPHONE_3_2 + #define __IPHONE_3_2 30200 +#endif +#ifndef __IPHONE_4_0 + #define __IPHONE_4_0 40000 +#endif +#ifndef __IPHONE_4_3 + #define __IPHONE_4_3 40300 +#endif +#ifndef __IPHONE_5_0 + #define __IPHONE_5_0 50000 +#endif + +// ---------------------------------------------------------------------------- +// CPP symbols that can be overridden in a prefix to control how the toolbox +// is compiled. +// ---------------------------------------------------------------------------- + + +// By setting the GTM_CONTAINERS_VALIDATION_FAILED_LOG and +// GTM_CONTAINERS_VALIDATION_FAILED_ASSERT macros you can control what happens +// when a validation fails. If you implement your own validators, you may want +// to control their internals using the same macros for consistency. +#ifndef GTM_CONTAINERS_VALIDATION_FAILED_ASSERT + #define GTM_CONTAINERS_VALIDATION_FAILED_ASSERT 0 +#endif + +// Give ourselves a consistent way to do inlines. Apple's macros even use +// a few different actual definitions, so we're based off of the foundation +// one. +#if !defined(GTM_INLINE) + #if (defined (__GNUC__) && (__GNUC__ == 4)) || defined (__clang__) + #define GTM_INLINE static __inline__ __attribute__((always_inline)) + #else + #define GTM_INLINE static __inline__ + #endif +#endif + +// Give ourselves a consistent way of doing externs that links up nicely +// when mixing objc and objc++ +#if !defined (GTM_EXTERN) + #if defined __cplusplus + #define GTM_EXTERN extern "C" + #define GTM_EXTERN_C_BEGIN extern "C" { + #define GTM_EXTERN_C_END } + #else + #define GTM_EXTERN extern + #define GTM_EXTERN_C_BEGIN + #define GTM_EXTERN_C_END + #endif +#endif + +// Give ourselves a consistent way of exporting things if we have visibility +// set to hidden. +#if !defined (GTM_EXPORT) + #define GTM_EXPORT __attribute__((visibility("default"))) +#endif + +// Give ourselves a consistent way of declaring something as unused. This +// doesn't use __unused because that is only supported in gcc 4.2 and greater. +#if !defined (GTM_UNUSED) +#define GTM_UNUSED(x) ((void)(x)) +#endif + +// _GTMDevLog & _GTMDevAssert +// +// _GTMDevLog & _GTMDevAssert are meant to be a very lightweight shell for +// developer level errors. This implementation simply macros to NSLog/NSAssert. +// It is not intended to be a general logging/reporting system. +// +// Please see http://code.google.com/p/google-toolbox-for-mac/wiki/DevLogNAssert +// for a little more background on the usage of these macros. +// +// _GTMDevLog log some error/problem in debug builds +// _GTMDevAssert assert if conditon isn't met w/in a method/function +// in all builds. +// +// To replace this system, just provide different macro definitions in your +// prefix header. Remember, any implementation you provide *must* be thread +// safe since this could be called by anything in what ever situtation it has +// been placed in. +// + +// We only define the simple macros if nothing else has defined this. +#ifndef _GTMDevLog + +#ifdef DEBUG + #define _GTMDevLog(...) NSLog(__VA_ARGS__) +#else + #define _GTMDevLog(...) do { } while (0) +#endif + +#endif // _GTMDevLog + +#ifndef _GTMDevAssert +// we directly invoke the NSAssert handler so we can pass on the varargs +// (NSAssert doesn't have a macro we can use that takes varargs) +#if !defined(NS_BLOCK_ASSERTIONS) + #define _GTMDevAssert(condition, ...) \ + do { \ + if (!(condition)) { \ + [[NSAssertionHandler currentHandler] \ + handleFailureInFunction:[NSString stringWithUTF8String:__PRETTY_FUNCTION__] \ + file:[NSString stringWithUTF8String:__FILE__] \ + lineNumber:__LINE__ \ + description:__VA_ARGS__]; \ + } \ + } while(0) +#else // !defined(NS_BLOCK_ASSERTIONS) + #define _GTMDevAssert(condition, ...) do { } while (0) +#endif // !defined(NS_BLOCK_ASSERTIONS) + +#endif // _GTMDevAssert + +// _GTMCompileAssert +// _GTMCompileAssert is an assert that is meant to fire at compile time if you +// want to check things at compile instead of runtime. For example if you +// want to check that a wchar is 4 bytes instead of 2 you would use +// _GTMCompileAssert(sizeof(wchar_t) == 4, wchar_t_is_4_bytes_on_OS_X) +// Note that the second "arg" is not in quotes, and must be a valid processor +// symbol in it's own right (no spaces, punctuation etc). + +// Wrapping this in an #ifndef allows external groups to define their own +// compile time assert scheme. +#ifndef _GTMCompileAssert + // We got this technique from here: + // http://unixjunkie.blogspot.com/2007/10/better-compile-time-asserts_29.html + + #define _GTMCompileAssertSymbolInner(line, msg) _GTMCOMPILEASSERT ## line ## __ ## msg + #define _GTMCompileAssertSymbol(line, msg) _GTMCompileAssertSymbolInner(line, msg) + #define _GTMCompileAssert(test, msg) \ + typedef char _GTMCompileAssertSymbol(__LINE__, msg) [ ((test) ? 1 : -1) ] +#endif // _GTMCompileAssert + +// ---------------------------------------------------------------------------- +// CPP symbols defined based on the project settings so the GTM code has +// simple things to test against w/o scattering the knowledge of project +// setting through all the code. +// ---------------------------------------------------------------------------- + +// Provide a single constant CPP symbol that all of GTM uses for ifdefing +// iPhone code. +#if TARGET_OS_IPHONE // iPhone SDK + // For iPhone specific stuff + #define GTM_IPHONE_SDK 1 + #if TARGET_IPHONE_SIMULATOR + #define GTM_IPHONE_SIMULATOR 1 + #else + #define GTM_IPHONE_DEVICE 1 + #endif // TARGET_IPHONE_SIMULATOR + // By default, GTM has provided it's own unittesting support, define this + // to use the support provided by Xcode, especially for the Xcode4 support + // for unittesting. + #ifndef GTM_IPHONE_USE_SENTEST + #define GTM_IPHONE_USE_SENTEST 0 + #endif +#else + // For MacOS specific stuff + #define GTM_MACOS_SDK 1 +#endif + +// Some of our own availability macros +#if GTM_MACOS_SDK +#define GTM_AVAILABLE_ONLY_ON_IPHONE UNAVAILABLE_ATTRIBUTE +#define GTM_AVAILABLE_ONLY_ON_MACOS +#else +#define GTM_AVAILABLE_ONLY_ON_IPHONE +#define GTM_AVAILABLE_ONLY_ON_MACOS UNAVAILABLE_ATTRIBUTE +#endif + +// GC was dropped by Apple, define the old constant incase anyone still keys +// off of it. +#ifndef GTM_SUPPORT_GC + #define GTM_SUPPORT_GC 0 +#endif + +// To simplify support for 64bit (and Leopard in general), we provide the type +// defines for non Leopard SDKs +#if !(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) + // NSInteger/NSUInteger and Max/Mins + #ifndef NSINTEGER_DEFINED + #if __LP64__ || NS_BUILD_32_LIKE_64 + typedef long NSInteger; + typedef unsigned long NSUInteger; + #else + typedef int NSInteger; + typedef unsigned int NSUInteger; + #endif + #define NSIntegerMax LONG_MAX + #define NSIntegerMin LONG_MIN + #define NSUIntegerMax ULONG_MAX + #define NSINTEGER_DEFINED 1 + #endif // NSINTEGER_DEFINED + // CGFloat + #ifndef CGFLOAT_DEFINED + #if defined(__LP64__) && __LP64__ + // This really is an untested path (64bit on Tiger?) + typedef double CGFloat; + #define CGFLOAT_MIN DBL_MIN + #define CGFLOAT_MAX DBL_MAX + #define CGFLOAT_IS_DOUBLE 1 + #else /* !defined(__LP64__) || !__LP64__ */ + typedef float CGFloat; + #define CGFLOAT_MIN FLT_MIN + #define CGFLOAT_MAX FLT_MAX + #define CGFLOAT_IS_DOUBLE 0 + #endif /* !defined(__LP64__) || !__LP64__ */ + #define CGFLOAT_DEFINED 1 + #endif // CGFLOAT_DEFINED +#endif // MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 + +// Some support for advanced clang static analysis functionality +// See http://clang-analyzer.llvm.org/annotations.html +#ifndef __has_feature // Optional. + #define __has_feature(x) 0 // Compatibility with non-clang compilers. +#endif + +#ifndef NS_RETURNS_RETAINED + #if __has_feature(attribute_ns_returns_retained) + #define NS_RETURNS_RETAINED __attribute__((ns_returns_retained)) + #else + #define NS_RETURNS_RETAINED + #endif +#endif + +#ifndef NS_RETURNS_NOT_RETAINED + #if __has_feature(attribute_ns_returns_not_retained) + #define NS_RETURNS_NOT_RETAINED __attribute__((ns_returns_not_retained)) + #else + #define NS_RETURNS_NOT_RETAINED + #endif +#endif + +#ifndef CF_RETURNS_RETAINED + #if __has_feature(attribute_cf_returns_retained) + #define CF_RETURNS_RETAINED __attribute__((cf_returns_retained)) + #else + #define CF_RETURNS_RETAINED + #endif +#endif + +#ifndef CF_RETURNS_NOT_RETAINED + #if __has_feature(attribute_cf_returns_not_retained) + #define CF_RETURNS_NOT_RETAINED __attribute__((cf_returns_not_retained)) + #else + #define CF_RETURNS_NOT_RETAINED + #endif +#endif + +#ifndef NS_CONSUMED + #if __has_feature(attribute_ns_consumed) + #define NS_CONSUMED __attribute__((ns_consumed)) + #else + #define NS_CONSUMED + #endif +#endif + +#ifndef CF_CONSUMED + #if __has_feature(attribute_cf_consumed) + #define CF_CONSUMED __attribute__((cf_consumed)) + #else + #define CF_CONSUMED + #endif +#endif + +#ifndef NS_CONSUMES_SELF + #if __has_feature(attribute_ns_consumes_self) + #define NS_CONSUMES_SELF __attribute__((ns_consumes_self)) + #else + #define NS_CONSUMES_SELF + #endif +#endif + +// Defined on 10.6 and above. +#ifndef NS_FORMAT_ARGUMENT + #define NS_FORMAT_ARGUMENT(A) +#endif + +// Defined on 10.6 and above. +#ifndef NS_FORMAT_FUNCTION + #define NS_FORMAT_FUNCTION(F,A) +#endif + +// Defined on 10.6 and above. +#ifndef CF_FORMAT_ARGUMENT + #define CF_FORMAT_ARGUMENT(A) +#endif + +// Defined on 10.6 and above. +#ifndef CF_FORMAT_FUNCTION + #define CF_FORMAT_FUNCTION(F,A) +#endif + +#ifndef GTM_NONNULL + #if defined(__has_attribute) + #if __has_attribute(nonnull) + #define GTM_NONNULL(x) __attribute__((nonnull x)) + #else + #define GTM_NONNULL(x) + #endif + #else + #define GTM_NONNULL(x) + #endif +#endif + +// Invalidates the initializer from which it's called. +#ifndef GTMInvalidateInitializer + #if __has_feature(objc_arc) + #define GTMInvalidateInitializer() \ + do { \ + [self class]; /* Avoid warning of dead store to |self|. */ \ + _GTMDevAssert(NO, @"Invalid initializer."); \ + return nil; \ + } while (0) + #else + #define GTMInvalidateInitializer() \ + do { \ + [self release]; \ + _GTMDevAssert(NO, @"Invalid initializer."); \ + return nil; \ + } while (0) + #endif +#endif + +#ifdef __OBJC__ + +// Declared here so that it can easily be used for logging tracking if +// necessary. See GTMUnitTestDevLog.h for details. +@class NSString; +GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...) NS_FORMAT_FUNCTION(1, 2); + +// Macro to allow you to create NSStrings out of other macros. +// #define FOO foo +// NSString *fooString = GTM_NSSTRINGIFY(FOO); +#if !defined (GTM_NSSTRINGIFY) + #define GTM_NSSTRINGIFY_INNER(x) @#x + #define GTM_NSSTRINGIFY(x) GTM_NSSTRINGIFY_INNER(x) +#endif + +// Macro to allow fast enumeration when building for 10.5 or later, and +// reliance on NSEnumerator for 10.4. Remember, NSDictionary w/ FastEnumeration +// does keys, so pick the right thing, nothing is done on the FastEnumeration +// side to be sure you're getting what you wanted. +#ifndef GTM_FOREACH_OBJECT + #if TARGET_OS_IPHONE || !(MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) + #define GTM_FOREACH_ENUMEREE(element, enumeration) \ + for (element in enumeration) + #define GTM_FOREACH_OBJECT(element, collection) \ + for (element in collection) + #define GTM_FOREACH_KEY(element, collection) \ + for (element in collection) + #else + #define GTM_FOREACH_ENUMEREE(element, enumeration) \ + for (NSEnumerator *_ ## element ## _enum = enumeration; \ + (element = [_ ## element ## _enum nextObject]) != nil; ) + #define GTM_FOREACH_OBJECT(element, collection) \ + GTM_FOREACH_ENUMEREE(element, [collection objectEnumerator]) + #define GTM_FOREACH_KEY(element, collection) \ + GTM_FOREACH_ENUMEREE(element, [collection keyEnumerator]) + #endif +#endif + +// ============================================================================ + +// To simplify support for both Leopard and Snow Leopard we declare +// the Snow Leopard protocols that we need here. +#if !defined(GTM_10_6_PROTOCOLS_DEFINED) && !(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) +#define GTM_10_6_PROTOCOLS_DEFINED 1 +@protocol NSConnectionDelegate +@end +@protocol NSAnimationDelegate +@end +@protocol NSImageDelegate +@end +@protocol NSTabViewDelegate +@end +#endif // !defined(GTM_10_6_PROTOCOLS_DEFINED) && !(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) + +// GTM_SEL_STRING is for specifying selector (usually property) names to KVC +// or KVO methods. +// In debug it will generate warnings for undeclared selectors if +// -Wunknown-selector is turned on. +// In release it will have no runtime overhead. +#ifndef GTM_SEL_STRING + #ifdef DEBUG + #define GTM_SEL_STRING(selName) NSStringFromSelector(@selector(selName)) + #else + #define GTM_SEL_STRING(selName) @#selName + #endif // DEBUG +#endif // GTM_SEL_STRING + +#endif // __OBJC__ diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTMGarbageCollection.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTMGarbageCollection.h new file mode 100644 index 0000000000..93d4efabf6 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTMGarbageCollection.h @@ -0,0 +1,72 @@ +// +// GTMGarbageCollection.h +// +// Copyright 2007-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import + +#import "GTMDefines.h" + +// This allows us to easily move our code from GC to non GC. +// They are no-ops unless we are require Leopard or above. +// See +// http://developer.apple.com/documentation/Cocoa/Conceptual/GarbageCollection/index.html +// and +// http://developer.apple.com/documentation/Cocoa/Conceptual/GarbageCollection/Articles/gcCoreFoundation.html#//apple_ref/doc/uid/TP40006687-SW1 +// for details. + +#if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) && !GTM_IPHONE_SDK +// General use would be to call this through GTMCFAutorelease +// but there may be a reason the you want to make something collectable +// but not autoreleased, especially in pure GC code where you don't +// want to bother with the nop autorelease. Done as a define instead of an +// inline so that tools like Clang's scan-build don't report code as leaking. +#define GTMNSMakeCollectable(cf) ((id)NSMakeCollectable(cf)) + +// GTMNSMakeUncollectable is for global maps, etc. that we don't +// want released ever. You should still retain these in non-gc code. +GTM_INLINE void GTMNSMakeUncollectable(id object) { + [[NSGarbageCollector defaultCollector] disableCollectorForPointer:object]; +} + +// Hopefully no code really needs this, but GTMIsGarbageCollectionEnabled is +// a common way to check at runtime if GC is on. +// There are some places where GC doesn't work w/ things w/in Apple's +// frameworks, so this is here so GTM unittests and detect it, and not run +// individual tests to work around bugs in Apple's frameworks. +GTM_INLINE BOOL GTMIsGarbageCollectionEnabled(void) { + return ([NSGarbageCollector defaultCollector] != nil); +} + +#else + +#define GTMNSMakeCollectable(cf) ((id)(cf)) + +GTM_INLINE void GTMNSMakeUncollectable(id object) { +} + +GTM_INLINE BOOL GTMIsGarbageCollectionEnabled(void) { + return NO; +} + +#endif + +// GTMCFAutorelease makes a CF object collectable in GC mode, or adds it +// to the autorelease pool in non-GC mode. Either way it is taken care +// of. Done as a define instead of an inline so that tools like Clang's +// scan-build don't report code as leaking. +#define GTMCFAutorelease(cf) ([GTMNSMakeCollectable(cf) autorelease]) + diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTMHTTPFetchHistory.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTMHTTPFetchHistory.h new file mode 100644 index 0000000000..96018f5d50 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTMHTTPFetchHistory.h @@ -0,0 +1,187 @@ +/* Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTMHTTPFetchHistory.h +// + +// +// Users of the GTMHTTPFetcher class may optionally create and set a fetch +// history object. The fetch history provides "memory" between subsequent +// fetches, including: +// +// - For fetch responses with Etag headers, the fetch history +// remembers the response headers. Future fetcher requests to the same URL +// will be given an "If-None-Match" header, telling the server to return +// a 304 Not Modified status if the response is unchanged, reducing the +// server load and network traffic. +// +// - Optionally, the fetch history can cache the ETagged data that was returned +// in the responses that contained Etag headers. If a later fetch +// results in a 304 status, the fetcher will return the cached ETagged data +// to the client along with a 200 status, hiding the 304. +// +// - The fetch history can track cookies. +// + +#pragma once + +#import + +#import "GTMHTTPFetcher.h" + +#undef _EXTERN +#undef _INITIALIZE_AS +#ifdef GTMHTTPFETCHHISTORY_DEFINE_GLOBALS + #define _EXTERN + #define _INITIALIZE_AS(x) =x +#else + #if defined(__cplusplus) + #define _EXTERN extern "C" + #else + #define _EXTERN extern + #endif + #define _INITIALIZE_AS(x) +#endif + + +// default data cache size for when we're caching responses to handle "not +// modified" errors for the client +#if GTM_IPHONE +// iPhone: up to 1MB memory +_EXTERN const NSUInteger kGTMDefaultETaggedDataCacheMemoryCapacity _INITIALIZE_AS(1*1024*1024); +#else +// Mac OS X: up to 15MB memory +_EXTERN const NSUInteger kGTMDefaultETaggedDataCacheMemoryCapacity _INITIALIZE_AS(15*1024*1024); +#endif + +// forward declarations +@class GTMURLCache; +@class GTMCookieStorage; + +@interface GTMHTTPFetchHistory : NSObject { + @private + GTMURLCache *etaggedDataCache_; + BOOL shouldRememberETags_; + BOOL shouldCacheETaggedData_; // if NO, then only headers are cached + GTMCookieStorage *cookieStorage_; +} + +// With caching enabled, previously-cached data will be returned instead of +// 304 Not Modified responses when repeating a fetch of an URL that previously +// included an ETag header in its response +@property (assign) BOOL shouldRememberETags; // default: NO +@property (assign) BOOL shouldCacheETaggedData; // default: NO + +// the default ETag data cache capacity is kGTMDefaultETaggedDataCacheMemoryCapacity +@property (assign) NSUInteger memoryCapacity; + +@property (retain) GTMCookieStorage *cookieStorage; + +- (id)initWithMemoryCapacity:(NSUInteger)totalBytes + shouldCacheETaggedData:(BOOL)shouldCacheETaggedData; + +- (void)updateRequest:(NSMutableURLRequest *)request isHTTPGet:(BOOL)isHTTPGet; + +- (void)clearETaggedDataCache; +- (void)clearHistory; + +- (void)removeAllCookies; + +@end + + +// GTMURLCache and GTMCachedURLResponse have interfaces similar to their +// NSURLCache counterparts, in hopes that someday the NSURLCache versions +// can be used. But in 10.5.8, those are not reliable enough except when +// used with +setSharedURLCache. Our goal here is just to cache +// responses for handling If-None-Match requests that return +// "Not Modified" responses, not for replacing the general URL +// caches. + +@interface GTMCachedURLResponse : NSObject { + @private + NSURLResponse *response_; + NSData *data_; + NSDate *useDate_; // date this response was last saved or used + NSDate *reservationDate_; // date this response's ETag was used +} + +@property (readonly) NSURLResponse* response; +@property (readonly) NSData* data; + +// date the response was saved or last accessed +@property (retain) NSDate *useDate; + +// date the response's ETag header was last used for a fetch request +@property (retain) NSDate *reservationDate; + +- (id)initWithResponse:(NSURLResponse *)response data:(NSData *)data; +@end + +@interface GTMURLCache : NSObject { + NSMutableDictionary *responses_; // maps request URL to GTMCachedURLResponse + NSUInteger memoryCapacity_; // capacity of NSDatas in the responses + NSUInteger totalDataSize_; // sum of sizes of NSDatas of all responses + NSTimeInterval reservationInterval_; // reservation expiration interval +} + +@property (assign) NSUInteger memoryCapacity; + +- (id)initWithMemoryCapacity:(NSUInteger)totalBytes; + +- (GTMCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request; +- (void)storeCachedResponse:(GTMCachedURLResponse *)cachedResponse forRequest:(NSURLRequest *)request; +- (void)removeCachedResponseForRequest:(NSURLRequest *)request; +- (void)removeAllCachedResponses; + +// for unit testing +- (void)setReservationInterval:(NSTimeInterval)secs; +- (NSDictionary *)responses; +- (NSUInteger)totalDataSize; +@end + +@interface GTMCookieStorage : NSObject { + @private + // The cookie storage object manages an array holding cookies, but the array + // is allocated externally (it may be in a fetcher object or the static + // fetcher cookie array.) See the fetcher's setCookieStorageMethod: + // for allocation of this object and assignment of its cookies array. + NSMutableArray *cookies_; +} + +// add all NSHTTPCookies in the supplied array to the storage array, +// replacing cookies in the storage array as appropriate +// Side effect: removes expired cookies from the storage array +- (void)setCookies:(NSArray *)newCookies; + +// retrieve all cookies appropriate for the given URL, considering +// domain, path, cookie name, expiration, security setting. +// Side effect: removes expired cookies from the storage array +- (NSArray *)cookiesForURL:(NSURL *)theURL; + +// return a cookie with the same name, domain, and path as the +// given cookie, or else return nil if none found +// +// Both the cookie being tested and all stored cookies should +// be valid (non-nil name, domains, paths) +- (NSHTTPCookie *)cookieMatchingCookie:(NSHTTPCookie *)cookie; + +// remove any expired cookies, excluding cookies with nil expirations +- (void)removeExpiredCookies; + +- (void)removeAllCookies; + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTMHTTPFetchHistory.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTMHTTPFetchHistory.m new file mode 100644 index 0000000000..2c859230f1 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTMHTTPFetchHistory.m @@ -0,0 +1,605 @@ +/* Copyright (c) 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTMHTTPFetchHistory.m +// + +#define GTMHTTPFETCHHISTORY_DEFINE_GLOBALS 1 + +#import "GTMHTTPFetchHistory.h" + +const NSTimeInterval kCachedURLReservationInterval = 60.0; // 1 minute +static NSString* const kGTMIfNoneMatchHeader = @"If-None-Match"; +static NSString* const kGTMETagHeader = @"Etag"; + +@implementation GTMCookieStorage + +- (id)init { + self = [super init]; + if (self != nil) { + cookies_ = [[NSMutableArray alloc] init]; + } + return self; +} + +- (void)dealloc { + [cookies_ release]; + [super dealloc]; +} + +// Add all cookies in the new cookie array to the storage, +// replacing stored cookies as appropriate. +// +// Side effect: removes expired cookies from the storage array. +- (void)setCookies:(NSArray *)newCookies { + + @synchronized(cookies_) { + [self removeExpiredCookies]; + + for (NSHTTPCookie *newCookie in newCookies) { + if ([[newCookie name] length] > 0 + && [[newCookie domain] length] > 0 + && [[newCookie path] length] > 0) { + + // remove the cookie if it's currently in the array + NSHTTPCookie *oldCookie = [self cookieMatchingCookie:newCookie]; + if (oldCookie) { + [cookies_ removeObjectIdenticalTo:oldCookie]; + } + + // make sure the cookie hasn't already expired + NSDate *expiresDate = [newCookie expiresDate]; + if ((!expiresDate) || [expiresDate timeIntervalSinceNow] > 0) { + [cookies_ addObject:newCookie]; + } + + } else { + NSAssert1(NO, @"Cookie incomplete: %@", newCookie); + } + } + } +} + +- (void)deleteCookie:(NSHTTPCookie *)cookie { + @synchronized(cookies_) { + NSHTTPCookie *foundCookie = [self cookieMatchingCookie:cookie]; + if (foundCookie) { + [cookies_ removeObjectIdenticalTo:foundCookie]; + } + } +} + +// Retrieve all cookies appropriate for the given URL, considering +// domain, path, cookie name, expiration, security setting. +// Side effect: removed expired cookies from the storage array. +- (NSArray *)cookiesForURL:(NSURL *)theURL { + + NSMutableArray *foundCookies = nil; + + @synchronized(cookies_) { + [self removeExpiredCookies]; + + // We'll prepend "." to the desired domain, since we want the + // actual domain "nytimes.com" to still match the cookie domain + // ".nytimes.com" when we check it below with hasSuffix. + NSString *host = [[theURL host] lowercaseString]; + NSString *path = [theURL path]; + NSString *scheme = [theURL scheme]; + + NSString *domain = nil; + BOOL isLocalhostRetrieval = NO; + + if ([host isEqual:@"localhost"]) { + isLocalhostRetrieval = YES; + } else { + if (host) { + domain = [@"." stringByAppendingString:host]; + } + } + + NSUInteger numberOfCookies = [cookies_ count]; + for (NSUInteger idx = 0; idx < numberOfCookies; idx++) { + + NSHTTPCookie *storedCookie = [cookies_ objectAtIndex:idx]; + + NSString *cookieDomain = [[storedCookie domain] lowercaseString]; + NSString *cookiePath = [storedCookie path]; + BOOL cookieIsSecure = [storedCookie isSecure]; + + BOOL isDomainOK; + + if (isLocalhostRetrieval) { + // prior to 10.5.6, the domain stored into NSHTTPCookies for localhost + // is "localhost.local" + isDomainOK = [cookieDomain isEqual:@"localhost"] + || [cookieDomain isEqual:@"localhost.local"]; + } else { + isDomainOK = [domain hasSuffix:cookieDomain]; + } + + BOOL isPathOK = [cookiePath isEqual:@"/"] || [path hasPrefix:cookiePath]; + BOOL isSecureOK = (!cookieIsSecure) || [scheme isEqual:@"https"]; + + if (isDomainOK && isPathOK && isSecureOK) { + if (foundCookies == nil) { + foundCookies = [NSMutableArray arrayWithCapacity:1]; + } + [foundCookies addObject:storedCookie]; + } + } + } + return foundCookies; +} + +// Return a cookie from the array with the same name, domain, and path as the +// given cookie, or else return nil if none found. +// +// Both the cookie being tested and all cookies in the storage array should +// be valid (non-nil name, domains, paths). +// +// Note: this should only be called from inside a @synchronized(cookies_) block +- (NSHTTPCookie *)cookieMatchingCookie:(NSHTTPCookie *)cookie { + + NSUInteger numberOfCookies = [cookies_ count]; + NSString *name = [cookie name]; + NSString *domain = [cookie domain]; + NSString *path = [cookie path]; + + NSAssert3(name && domain && path, @"Invalid cookie (name:%@ domain:%@ path:%@)", + name, domain, path); + + for (NSUInteger idx = 0; idx < numberOfCookies; idx++) { + + NSHTTPCookie *storedCookie = [cookies_ objectAtIndex:idx]; + + if ([[storedCookie name] isEqual:name] + && [[storedCookie domain] isEqual:domain] + && [[storedCookie path] isEqual:path]) { + + return storedCookie; + } + } + return nil; +} + + +// Internal routine to remove any expired cookies from the array, excluding +// cookies with nil expirations. +// +// Note: this should only be called from inside a @synchronized(cookies_) block +- (void)removeExpiredCookies { + + // count backwards since we're deleting items from the array + for (NSInteger idx = (NSInteger)[cookies_ count] - 1; idx >= 0; idx--) { + + NSHTTPCookie *storedCookie = [cookies_ objectAtIndex:(NSUInteger)idx]; + + NSDate *expiresDate = [storedCookie expiresDate]; + if (expiresDate && [expiresDate timeIntervalSinceNow] < 0) { + [cookies_ removeObjectAtIndex:(NSUInteger)idx]; + } + } +} + +- (void)removeAllCookies { + @synchronized(cookies_) { + [cookies_ removeAllObjects]; + } +} +@end + +// +// GTMCachedURLResponse +// + +@implementation GTMCachedURLResponse + +@synthesize response = response_; +@synthesize data = data_; +@synthesize reservationDate = reservationDate_; +@synthesize useDate = useDate_; + +- (id)initWithResponse:(NSURLResponse *)response data:(NSData *)data { + self = [super init]; + if (self != nil) { + response_ = [response retain]; + data_ = [data retain]; + useDate_ = [[NSDate alloc] init]; + } + return self; +} + +- (void)dealloc { + [response_ release]; + [data_ release]; + [useDate_ release]; + [reservationDate_ release]; + [super dealloc]; +} + +- (NSString *)description { + NSString *reservationStr = reservationDate_ ? + [NSString stringWithFormat:@" resDate:%@", reservationDate_] : @""; + + return [NSString stringWithFormat:@"%@ %p: {bytes:%@ useDate:%@%@}", + [self class], self, + data_ ? [NSNumber numberWithInt:(int)[data_ length]] : nil, + useDate_, + reservationStr]; +} + +- (NSComparisonResult)compareUseDate:(GTMCachedURLResponse *)other { + return [useDate_ compare:[other useDate]]; +} + +@end + +// +// GTMURLCache +// + +@implementation GTMURLCache + +@dynamic memoryCapacity; + +- (id)init { + return [self initWithMemoryCapacity:kGTMDefaultETaggedDataCacheMemoryCapacity]; +} + +- (id)initWithMemoryCapacity:(NSUInteger)totalBytes { + self = [super init]; + if (self != nil) { + memoryCapacity_ = totalBytes; + + responses_ = [[NSMutableDictionary alloc] initWithCapacity:5]; + + reservationInterval_ = kCachedURLReservationInterval; + } + return self; +} + +- (void)dealloc { + [responses_ release]; + [super dealloc]; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"%@ %p: {responses:%@}", + [self class], self, [responses_ allValues]]; +} + +// Setters/getters + +- (void)pruneCacheResponses { + // Internal routine to remove the least-recently-used responses when the + // cache has grown too large + if (memoryCapacity_ >= totalDataSize_) return; + + // Sort keys by date + SEL sel = @selector(compareUseDate:); + NSArray *sortedKeys = [responses_ keysSortedByValueUsingSelector:sel]; + + // The least-recently-used keys are at the beginning of the sorted array; + // remove those (except ones still reserved) until the total data size is + // reduced sufficiently + for (NSURL *key in sortedKeys) { + GTMCachedURLResponse *response = [responses_ objectForKey:key]; + + NSDate *resDate = [response reservationDate]; + BOOL isResponseReserved = (resDate != nil) + && ([resDate timeIntervalSinceNow] > -reservationInterval_); + + if (!isResponseReserved) { + // We can remove this response from the cache + NSUInteger storedSize = [[response data] length]; + totalDataSize_ -= storedSize; + [responses_ removeObjectForKey:key]; + } + + // If we've removed enough response data, then we're done + if (memoryCapacity_ >= totalDataSize_) break; + } +} + +- (void)storeCachedResponse:(GTMCachedURLResponse *)cachedResponse + forRequest:(NSURLRequest *)request { + @synchronized(self) { + // Remove any previous entry for this request + [self removeCachedResponseForRequest:request]; + + // cache this one only if it's not bigger than our cache + NSUInteger storedSize = [[cachedResponse data] length]; + if (storedSize < memoryCapacity_) { + + NSURL *key = [request URL]; + [responses_ setObject:cachedResponse forKey:key]; + totalDataSize_ += storedSize; + + [self pruneCacheResponses]; + } + } +} + +- (GTMCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request { + GTMCachedURLResponse *response; + + @synchronized(self) { + NSURL *key = [request URL]; + response = [[[responses_ objectForKey:key] retain] autorelease]; + + // Touch the date to indicate this was recently retrieved + [response setUseDate:[NSDate date]]; + } + return response; +} + +- (void)removeCachedResponseForRequest:(NSURLRequest *)request { + @synchronized(self) { + NSURL *key = [request URL]; + totalDataSize_ -= [[[responses_ objectForKey:key] data] length]; + [responses_ removeObjectForKey:key]; + } +} + +- (void)removeAllCachedResponses { + @synchronized(self) { + [responses_ removeAllObjects]; + totalDataSize_ = 0; + } +} + +- (NSUInteger)memoryCapacity { + return memoryCapacity_; +} + +- (void)setMemoryCapacity:(NSUInteger)totalBytes { + @synchronized(self) { + BOOL didShrink = (totalBytes < memoryCapacity_); + memoryCapacity_ = totalBytes; + + if (didShrink) { + [self pruneCacheResponses]; + } + } +} + +// Methods for unit testing. +- (void)setReservationInterval:(NSTimeInterval)secs { + reservationInterval_ = secs; +} + +- (NSDictionary *)responses { + return responses_; +} + +- (NSUInteger)totalDataSize { + return totalDataSize_; +} + +@end + +// +// GTMHTTPFetchHistory +// + +@interface GTMHTTPFetchHistory () +- (NSString *)cachedETagForRequest:(NSURLRequest *)request; +- (void)removeCachedDataForRequest:(NSURLRequest *)request; +@end + +@implementation GTMHTTPFetchHistory + +@synthesize cookieStorage = cookieStorage_; + +@dynamic shouldRememberETags; +@dynamic shouldCacheETaggedData; +@dynamic memoryCapacity; + +- (id)init { + return [self initWithMemoryCapacity:kGTMDefaultETaggedDataCacheMemoryCapacity + shouldCacheETaggedData:NO]; +} + +- (id)initWithMemoryCapacity:(NSUInteger)totalBytes + shouldCacheETaggedData:(BOOL)shouldCacheETaggedData { + self = [super init]; + if (self != nil) { + etaggedDataCache_ = [[GTMURLCache alloc] initWithMemoryCapacity:totalBytes]; + shouldRememberETags_ = shouldCacheETaggedData; + shouldCacheETaggedData_ = shouldCacheETaggedData; + cookieStorage_ = [[GTMCookieStorage alloc] init]; + } + return self; +} + +- (void)dealloc { + [etaggedDataCache_ release]; + [cookieStorage_ release]; + [super dealloc]; +} + +- (void)updateRequest:(NSMutableURLRequest *)request isHTTPGet:(BOOL)isHTTPGet { + @synchronized(self) { + if ([self shouldRememberETags]) { + // If this URL is in the history, and no ETag has been set, then + // set the ETag header field + + // If we have a history, we're tracking across fetches, so we don't + // want to pull results from any other cache + [request setCachePolicy:NSURLRequestReloadIgnoringCacheData]; + + if (isHTTPGet) { + // We'll only add an ETag if there's no ETag specified in the user's + // request + NSString *specifiedETag = [request valueForHTTPHeaderField:kGTMIfNoneMatchHeader]; + if (specifiedETag == nil) { + // No ETag: extract the previous ETag for this request from the + // fetch history, and add it to the request + NSString *cachedETag = [self cachedETagForRequest:request]; + + if (cachedETag != nil) { + [request addValue:cachedETag forHTTPHeaderField:kGTMIfNoneMatchHeader]; + } + } else { + // Has an ETag: remove any stored response in the fetch history + // for this request, as the If-None-Match header could lead to + // a 304 Not Modified, and we want that error delivered to the + // user since they explicitly specified the ETag + [self removeCachedDataForRequest:request]; + } + } + } + } +} + +- (void)updateFetchHistoryWithRequest:(NSURLRequest *)request + response:(NSURLResponse *)response + downloadedData:(NSData *)downloadedData { + @synchronized(self) { + if (![self shouldRememberETags]) return; + + if (![response respondsToSelector:@selector(allHeaderFields)]) return; + + NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode]; + + if (statusCode != kGTMHTTPFetcherStatusNotModified) { + // Save this ETag string for successful results (<300) + // If there's no last modified string, clear the dictionary + // entry for this URL. Also cache or delete the data, if appropriate + // (when etaggedDataCache is non-nil.) + NSDictionary *headers = [(NSHTTPURLResponse *)response allHeaderFields]; + NSString* etag = [headers objectForKey:kGTMETagHeader]; + + if (etag != nil && statusCode < 300) { + + // we want to cache responses for the headers, even if the client + // doesn't want the response body data caches + NSData *dataToStore = shouldCacheETaggedData_ ? downloadedData : nil; + + GTMCachedURLResponse *cachedResponse; + cachedResponse = [[[GTMCachedURLResponse alloc] initWithResponse:response + data:dataToStore] autorelease]; + [etaggedDataCache_ storeCachedResponse:cachedResponse + forRequest:request]; + } else { + [etaggedDataCache_ removeCachedResponseForRequest:request]; + } + } + } +} + +- (NSString *)cachedETagForRequest:(NSURLRequest *)request { + // Internal routine. + GTMCachedURLResponse *cachedResponse; + cachedResponse = [etaggedDataCache_ cachedResponseForRequest:request]; + + NSURLResponse *response = [cachedResponse response]; + NSDictionary *headers = [(NSHTTPURLResponse *)response allHeaderFields]; + NSString *cachedETag = [headers objectForKey:kGTMETagHeader]; + if (cachedETag) { + // Since the request having an ETag implies this request is about + // to be fetched again, reserve the cached response to ensure that + // that it will be around at least until the fetch completes. + // + // When the fetch completes, either the cached response will be replaced + // with a new response, or the cachedDataForRequest: method below will + // clear the reservation. + [cachedResponse setReservationDate:[NSDate date]]; + } + return cachedETag; +} + +- (NSData *)cachedDataForRequest:(NSURLRequest *)request { + @synchronized(self) { + GTMCachedURLResponse *cachedResponse; + cachedResponse = [etaggedDataCache_ cachedResponseForRequest:request]; + + NSData *cachedData = [cachedResponse data]; + + // Since the data for this cached request is being obtained from the cache, + // we can clear the reservation as the fetch has completed. + [cachedResponse setReservationDate:nil]; + + return cachedData; + } +} + +- (void)removeCachedDataForRequest:(NSURLRequest *)request { + @synchronized(self) { + [etaggedDataCache_ removeCachedResponseForRequest:request]; + } +} + +- (void)clearETaggedDataCache { + @synchronized(self) { + [etaggedDataCache_ removeAllCachedResponses]; + } +} + +- (void)clearHistory { + @synchronized(self) { + [self clearETaggedDataCache]; + [cookieStorage_ removeAllCookies]; + } +} + +- (void)removeAllCookies { + @synchronized(self) { + [cookieStorage_ removeAllCookies]; + } +} + +- (BOOL)shouldRememberETags { + return shouldRememberETags_; +} + +- (void)setShouldRememberETags:(BOOL)flag { + BOOL wasRemembering = shouldRememberETags_; + shouldRememberETags_ = flag; + + if (wasRemembering && !flag) { + // Free up the cache memory + [self clearETaggedDataCache]; + } +} + +- (BOOL)shouldCacheETaggedData { + return shouldCacheETaggedData_; +} + +- (void)setShouldCacheETaggedData:(BOOL)flag { + BOOL wasCaching = shouldCacheETaggedData_; + shouldCacheETaggedData_ = flag; + + if (flag) { + self.shouldRememberETags = YES; + } + + if (wasCaching && !flag) { + // users expect turning off caching to free up the cache memory + [self clearETaggedDataCache]; + } +} + +- (NSUInteger)memoryCapacity { + return [etaggedDataCache_ memoryCapacity]; +} + +- (void)setMemoryCapacity:(NSUInteger)totalBytes { + [etaggedDataCache_ setMemoryCapacity:totalBytes]; +} + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTMHTTPFetcher.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTMHTTPFetcher.h new file mode 100644 index 0000000000..e497737073 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTMHTTPFetcher.h @@ -0,0 +1,748 @@ +/* Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTMHTTPFetcher.h +// + +// This is essentially a wrapper around NSURLConnection for POSTs and GETs. +// If setPostData: is called, then POST is assumed. +// +// When would you use this instead of NSURLConnection? +// +// - When you just want the result from a GET, POST, or PUT +// - When you want the "standard" behavior for connections (redirection handling +// an so on) +// - When you want automatic retry on failures +// - When you want to avoid cookie collisions with Safari and other applications +// - When you are fetching resources with ETags and want to avoid the overhead +// of repeated fetches of unchanged data +// - When you need to set a credential for the http operation +// +// This is assumed to be a one-shot fetch request; don't reuse the object +// for a second fetch. +// +// The fetcher may be created auto-released, in which case it will release +// itself after the fetch completion callback. The fetcher is implicitly +// retained as long as a connection is pending. +// +// But if you may need to cancel the fetcher, retain it and have the delegate +// release the fetcher in the callbacks. +// +// Sample usage: +// +// NSURLRequest *request = [NSURLRequest requestWithURL:myURL]; +// GTMHTTPFetcher* myFetcher = [GTMHTTPFetcher fetcherWithRequest:request]; +// +// // optional upload body data +// [myFetcher setPostData:[postString dataUsingEncoding:NSUTF8StringEncoding]]; +// +// [myFetcher beginFetchWithDelegate:self +// didFinishSelector:@selector(myFetcher:finishedWithData:error:)]; +// +// Upon fetch completion, the callback selector is invoked; it should have +// this signature (you can use any callback method name you want so long as +// the signature matches this): +// +// - (void)myFetcher:(GTMHTTPFetcher *)fetcher finishedWithData:(NSData *)retrievedData error:(NSError *)error; +// +// The block callback version looks like: +// +// [myFetcher beginFetchWithCompletionHandler:^(NSData *retrievedData, NSError *error) { +// if (error != nil) { +// // status code or network error +// } else { +// // succeeded +// } +// }]; + +// +// NOTE: Fetches may retrieve data from the server even though the server +// returned an error. The failure selector is called when the server +// status is >= 300, with an NSError having domain +// kGTMHTTPFetcherStatusDomain and code set to the server status. +// +// Status codes are at +// +// +// Threading and queue support: +// +// Callbacks require either that the thread used to start the fetcher have a run +// loop spinning (typically the main thread), or that an NSOperationQueue be +// provided upon which the delegate callbacks will be called. Starting with +// 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]; +// +// or specify the main queue for callbacks on the main thread: +// +// fetcher.delegateQueue = [NSOperationQueue mainQueue]; +// +// The client may also re-dispatch from the callbacks and notifications to +// a known dispatch queue: +// +// [myFetcher beginFetchWithCompletionHandler:^(NSData *retrievedData, NSError *error) { +// if (error == nil) { +// dispatch_async(myDispatchQueue, ^{ +// ... +// }); +// } +// }]; +// +// +// +// Downloading to disk: +// +// To have downloaded data saved directly to disk, specify either a path for the +// downloadPath property, or a file handle for the downloadFileHandle property. +// When downloading to disk, callbacks will be passed a nil for the NSData* +// arguments. +// +// +// HTTP methods and headers: +// +// Alternative HTTP methods, like PUT, and custom headers can be specified by +// creating the fetcher with an appropriate NSMutableURLRequest +// +// +// Proxies: +// +// Proxy handling is invisible so long as the system has a valid credential in +// the keychain, which is normally true (else most NSURL-based apps would have +// difficulty.) But when there is a proxy authetication error, the the fetcher +// will call the failedWithError: method with the NSURLChallenge in the error's +// userInfo. The error method can get the challenge info like this: +// +// NSURLAuthenticationChallenge *challenge +// = [[error userInfo] objectForKey:kGTMHTTPFetcherErrorChallengeKey]; +// BOOL isProxyChallenge = [[challenge protectionSpace] isProxy]; +// +// If a proxy error occurs, you can ask the user for the proxy username/password +// and call fetcher's setProxyCredential: to provide those for the +// next attempt to fetch. +// +// +// Cookies: +// +// There are three supported mechanisms for remembering cookies between fetches. +// +// By default, GTMHTTPFetcher uses a mutable array held statically to track +// cookies for all instantiated fetchers. This avoids server cookies being set +// by servers for the application from interfering with Safari cookie settings, +// and vice versa. The fetcher cookies are lost when the application quits. +// +// To rely instead on WebKit's global NSHTTPCookieStorage, call +// setCookieStorageMethod: with kGTMHTTPFetcherCookieStorageMethodSystemDefault. +// +// If the fetcher is created from a GTMHTTPFetcherService object +// then the cookie storage mechanism is set to use the cookie storage in the +// service object rather than the static storage. +// +// +// Fetching for periodic checks: +// +// The fetcher object tracks ETag headers from responses and +// provide an "If-None-Match" header. This allows the server to save +// bandwidth by providing a status message instead of repeated response +// data. +// +// To get this behavior, create the fetcher from an GTMHTTPFetcherService object +// and look for a fetch callback error with code 304 +// (kGTMHTTPFetcherStatusNotModified) like this: +// +// - (void)myFetcher:(GTMHTTPFetcher *)fetcher finishedWithData:(NSData *)data error:(NSError *)error { +// if ([error code] == kGTMHTTPFetcherStatusNotModified) { +// // |data| is empty; use the data from the previous finishedWithData: for this URL +// } else { +// // handle other server status code +// } +// } +// +// +// Monitoring received data +// +// The optional received data selector can be set with setReceivedDataSelector: +// and should have the signature +// +// - (void)myFetcher:(GTMHTTPFetcher *)fetcher receivedData:(NSData *)dataReceivedSoFar; +// +// The number bytes received so far is available as [fetcher downloadedLength]. +// This number may go down if a redirect causes the download to begin again from +// a new server. +// +// If supplied by the server, the anticipated total download size is available +// as [[myFetcher response] expectedContentLength] (and may be -1 for unknown +// download sizes.) +// +// +// Automatic retrying of fetches +// +// The fetcher can optionally create a timer and reattempt certain kinds of +// fetch failures (status codes 408, request timeout; 503, service unavailable; +// 504, gateway timeout; networking errors NSURLErrorTimedOut and +// NSURLErrorNetworkConnectionLost.) The user may set a retry selector to +// customize the type of errors which will be retried. +// +// Retries are done in an exponential-backoff fashion (that is, after 1 second, +// 2, 4, 8, and so on.) +// +// Enabling automatic retries looks like this: +// [myFetcher setRetryEnabled:YES]; +// +// With retries enabled, the success or failure callbacks are called only +// when no more retries will be attempted. Calling the fetcher's stopFetching +// method will terminate the retry timer, without the finished or failure +// selectors being invoked. +// +// Optionally, the client may set the maximum retry interval: +// [myFetcher setMaxRetryInterval:60.0]; // in seconds; default is 60 seconds +// // for downloads, 600 for uploads +// +// Also optionally, the client may provide a callback selector to determine +// if a status code or other error should be retried. +// [myFetcher setRetrySelector:@selector(myFetcher:willRetry:forError:)]; +// +// If set, the retry selector should have the signature: +// -(BOOL)fetcher:(GTMHTTPFetcher *)fetcher willRetry:(BOOL)suggestedWillRetry forError:(NSError *)error +// and return YES to set the retry timer or NO to fail without additional +// fetch attempts. +// +// The retry method may return the |suggestedWillRetry| argument to get the +// default retry behavior. Server status codes are present in the +// error argument, and have the domain kGTMHTTPFetcherStatusDomain. The +// user's method may look something like this: +// +// -(BOOL)myFetcher:(GTMHTTPFetcher *)fetcher willRetry:(BOOL)suggestedWillRetry forError:(NSError *)error { +// +// // perhaps examine [error domain] and [error code], or [fetcher retryCount] +// // +// // return YES to start the retry timer, NO to proceed to the failure +// // callback, or |suggestedWillRetry| to get default behavior for the +// // current error domain and code values. +// return suggestedWillRetry; +// } + + + +#pragma once + +#import + +#if defined(GTL_TARGET_NAMESPACE) + // we're using target namespace macros + #import "GTLDefines.h" +#elif defined(GDATA_TARGET_NAMESPACE) + #import "GDataDefines.h" +#else + #if TARGET_OS_IPHONE + #ifndef GTM_FOUNDATION_ONLY + #define GTM_FOUNDATION_ONLY 1 + #endif + #ifndef GTM_IPHONE + #define GTM_IPHONE 1 + #endif + #endif +#endif + +#if TARGET_OS_IPHONE && (__IPHONE_OS_VERSION_MAX_ALLOWED >= 40000) + #define GTM_BACKGROUND_FETCHING 1 +#endif + +#undef _EXTERN +#undef _INITIALIZE_AS +#ifdef GTMHTTPFETCHER_DEFINE_GLOBALS + #define _EXTERN + #define _INITIALIZE_AS(x) =x +#else + #if defined(__cplusplus) + #define _EXTERN extern "C" + #else + #define _EXTERN extern + #endif + #define _INITIALIZE_AS(x) +#endif + +// notifications +// +// fetch started and stopped, and fetch retry delay started and stopped +_EXTERN NSString* const kGTMHTTPFetcherStartedNotification _INITIALIZE_AS(@"kGTMHTTPFetcherStartedNotification"); +_EXTERN NSString* const kGTMHTTPFetcherStoppedNotification _INITIALIZE_AS(@"kGTMHTTPFetcherStoppedNotification"); +_EXTERN NSString* const kGTMHTTPFetcherRetryDelayStartedNotification _INITIALIZE_AS(@"kGTMHTTPFetcherRetryDelayStartedNotification"); +_EXTERN NSString* const kGTMHTTPFetcherRetryDelayStoppedNotification _INITIALIZE_AS(@"kGTMHTTPFetcherRetryDelayStoppedNotification"); + +// callback constants +_EXTERN NSString* const kGTMHTTPFetcherErrorDomain _INITIALIZE_AS(@"com.google.GTMHTTPFetcher"); +_EXTERN NSString* const kGTMHTTPFetcherStatusDomain _INITIALIZE_AS(@"com.google.HTTPStatus"); +_EXTERN NSString* const kGTMHTTPFetcherErrorChallengeKey _INITIALIZE_AS(@"challenge"); +_EXTERN NSString* const kGTMHTTPFetcherStatusDataKey _INITIALIZE_AS(@"data"); // data returned with a kGTMHTTPFetcherStatusDomain error + +enum { + kGTMHTTPFetcherErrorDownloadFailed = -1, + kGTMHTTPFetcherErrorAuthenticationChallengeFailed = -2, + kGTMHTTPFetcherErrorChunkUploadFailed = -3, + kGTMHTTPFetcherErrorFileHandleException = -4, + kGTMHTTPFetcherErrorBackgroundExpiration = -6, + + // The code kGTMHTTPFetcherErrorAuthorizationFailed (-5) has been removed; + // look for status 401 instead. + + kGTMHTTPFetcherStatusNotModified = 304, + kGTMHTTPFetcherStatusBadRequest = 400, + kGTMHTTPFetcherStatusUnauthorized = 401, + kGTMHTTPFetcherStatusForbidden = 403, + kGTMHTTPFetcherStatusPreconditionFailed = 412 +}; + +// cookie storage methods +enum { + kGTMHTTPFetcherCookieStorageMethodStatic = 0, + kGTMHTTPFetcherCookieStorageMethodFetchHistory = 1, + kGTMHTTPFetcherCookieStorageMethodSystemDefault = 2, + kGTMHTTPFetcherCookieStorageMethodNone = 3 +}; + +#ifdef __cplusplus +extern "C" { +#endif + +void GTMAssertSelectorNilOrImplementedWithArgs(id obj, SEL sel, ...); + +// Utility functions for applications self-identifying to servers via a +// user-agent header + +// Make a proper app name without whitespace from the given string, removing +// whitespace and other characters that may be special parsed marks of +// the full user-agent string. +NSString *GTMCleanedUserAgentString(NSString *str); + +// Make an identifier like "MacOSX/10.7.1" or "iPod_Touch/4.1" +NSString *GTMSystemVersionString(void); + +// Make a generic name and version for the current application, like +// com.example.MyApp/1.2.3 relying on the bundle identifier and the +// CFBundleShortVersionString or CFBundleVersion. If no bundle ID +// is available, the process name preceded by "proc_" is used. +NSString *GTMApplicationIdentifier(NSBundle *bundle); + +#ifdef __cplusplus +} // extern "C" +#endif + +@class GTMHTTPFetcher; + +@protocol GTMCookieStorageProtocol +// This protocol allows us to call into the service without requiring +// GTMCookieStorage sources in this project +// +// The public interface for cookie handling is the GTMCookieStorage class, +// accessible from a fetcher service object's fetchHistory or from the fetcher's +// +staticCookieStorage method. +- (NSArray *)cookiesForURL:(NSURL *)theURL; +- (void)setCookies:(NSArray *)newCookies; +@end + +@protocol GTMHTTPFetchHistoryProtocol +// This protocol allows us to call the fetch history object without requiring +// GTMHTTPFetchHistory sources in this project +- (void)updateRequest:(NSMutableURLRequest *)request isHTTPGet:(BOOL)isHTTPGet; +- (BOOL)shouldCacheETaggedData; +- (NSData *)cachedDataForRequest:(NSURLRequest *)request; +- (id )cookieStorage; +- (void)updateFetchHistoryWithRequest:(NSURLRequest *)request + response:(NSURLResponse *)response + downloadedData:(NSData *)downloadedData; +- (void)removeCachedDataForRequest:(NSURLRequest *)request; +@end + +@protocol GTMHTTPFetcherServiceProtocol +// This protocol allows us to call into the service without requiring +// GTMHTTPFetcherService sources in this project + +@property (retain) NSOperationQueue *delegateQueue; + +- (BOOL)fetcherShouldBeginFetching:(GTMHTTPFetcher *)fetcher; +- (void)fetcherDidStop:(GTMHTTPFetcher *)fetcher; + +- (GTMHTTPFetcher *)fetcherWithRequest:(NSURLRequest *)request; +- (BOOL)isDelayingFetcher:(GTMHTTPFetcher *)fetcher; +@end + +@protocol GTMFetcherAuthorizationProtocol +@required +// This protocol allows us to call the authorizer without requiring its sources +// in this project +- (void)authorizeRequest:(NSMutableURLRequest *)request + delegate:(id)delegate + didFinishSelector:(SEL)sel; + +- (void)stopAuthorization; + +- (void)stopAuthorizationForRequest:(NSURLRequest *)request; + +- (BOOL)isAuthorizingRequest:(NSURLRequest *)request; + +- (BOOL)isAuthorizedRequest:(NSURLRequest *)request; + +- (NSString *)userEmail; + +@optional +@property (assign) id fetcherService; // WEAK + +- (BOOL)primeForRefresh; +@end + +// GTMHTTPFetcher objects are used for async retrieval of an http get or post +// +// See additional comments at the beginning of this file +@interface GTMHTTPFetcher : NSObject { + @protected + NSMutableURLRequest *request_; + NSURLConnection *connection_; + NSMutableData *downloadedData_; + NSString *downloadPath_; + NSString *temporaryDownloadPath_; + NSFileHandle *downloadFileHandle_; + unsigned long long downloadedLength_; + NSURLCredential *credential_; // username & password + NSURLCredential *proxyCredential_; // credential supplied to proxy servers + NSData *postData_; + NSInputStream *postStream_; + NSMutableData *loggedStreamData_; + NSURLResponse *response_; // set in connection:didReceiveResponse: + id delegate_; + SEL finishedSel_; // should by implemented by delegate + SEL sentDataSel_; // optional, set with setSentDataSelector + SEL receivedDataSel_; // optional, set with setReceivedDataSelector +#if NS_BLOCKS_AVAILABLE + void (^completionBlock_)(NSData *, NSError *); + void (^receivedDataBlock_)(NSData *); + void (^sentDataBlock_)(NSInteger, NSInteger, NSInteger); + BOOL (^retryBlock_)(BOOL, NSError *); +#elif !__LP64__ + // placeholders: for 32-bit builds, keep the size of the object's ivar section + // the same with and without blocks + id completionPlaceholder_; + id receivedDataPlaceholder_; + id sentDataPlaceholder_; + id retryPlaceholder_; +#endif + BOOL hasConnectionEnded_; // set if the connection need not be cancelled + BOOL isCancellingChallenge_; // set only when cancelling an auth challenge + BOOL isStopNotificationNeeded_; // set when start notification has been sent + BOOL shouldFetchInBackground_; +#if GTM_BACKGROUND_FETCHING + NSUInteger backgroundTaskIdentifer_; // UIBackgroundTaskIdentifier +#endif + id userData_; // retained, if set by caller + NSMutableDictionary *properties_; // more data retained for caller + NSArray *runLoopModes_; // optional + NSOperationQueue *delegateQueue_; // optional; available iOS 6/10.7 and later + id fetchHistory_; // if supplied by the caller, used for Last-Modified-Since checks and cookies + NSInteger cookieStorageMethod_; // constant from above + id cookieStorage_; + + id authorizer_; + + // the service object that created and monitors this fetcher, if any + id service_; + NSString *serviceHost_; + NSInteger servicePriority_; + NSThread *thread_; + + BOOL isRetryEnabled_; // user wants auto-retry + SEL retrySel_; // optional; set with setRetrySelector + NSTimer *retryTimer_; + NSUInteger retryCount_; + NSTimeInterval maxRetryInterval_; // default 600 seconds + NSTimeInterval minRetryInterval_; // random between 1 and 2 seconds + NSTimeInterval retryFactor_; // default interval multiplier is 2 + NSTimeInterval lastRetryInterval_; + BOOL hasAttemptedAuthRefresh_; + + NSString *comment_; // comment for log + NSString *log_; +#if !STRIP_GTM_FETCH_LOGGING + NSString *logRequestBody_; + NSString *logResponseBody_; + BOOL shouldDeferResponseBodyLogging_; +#endif +} + +// Create a fetcher +// +// fetcherWithRequest will return an autoreleased fetcher, but if +// the connection is successfully created, the connection should retain the +// fetcher for the life of the connection as well. So the caller doesn't have +// to retain the fetcher explicitly unless they want to be able to cancel it. ++ (GTMHTTPFetcher *)fetcherWithRequest:(NSURLRequest *)request; + +// Convenience methods that make a request, like +fetcherWithRequest ++ (GTMHTTPFetcher *)fetcherWithURL:(NSURL *)requestURL; ++ (GTMHTTPFetcher *)fetcherWithURLString:(NSString *)requestURLString; + +// Designated initializer +- (id)initWithRequest:(NSURLRequest *)request; + +// Fetcher request +// +// The underlying request is mutable and may be modified by the caller +@property (retain) NSMutableURLRequest *mutableRequest; + +// Setting the credential is optional; it is used if the connection receives +// an authentication challenge +@property (retain) NSURLCredential *credential; + +// Setting the proxy credential is optional; it is used if the connection +// receives an authentication challenge from a proxy +@property (retain) NSURLCredential *proxyCredential; + +// If post data or stream is not set, then a GET retrieval method is assumed +@property (retain) NSData *postData; +@property (retain) NSInputStream *postStream; + +// The default cookie storage method is kGTMHTTPFetcherCookieStorageMethodStatic +// without a fetch history set, and kGTMHTTPFetcherCookieStorageMethodFetchHistory +// with a fetch history set +// +// Applications needing control of cookies across a sequence of fetches should +// create fetchers from a GTMHTTPFetcherService object (which encapsulates +// fetch history) for a well-defined cookie store +@property (assign) NSInteger cookieStorageMethod; + ++ (id )staticCookieStorage; + +// Object to add authorization to the request, if needed +@property (retain) id authorizer; + +// The service object that created and monitors this fetcher, if any +@property (retain) id service; + +// The host, if any, used to classify this fetcher in the fetcher service +@property (copy) NSString *serviceHost; + +// The priority, if any, used for starting fetchers in the fetcher service +// +// Lower values are higher priority; the default is 0, and values may +// be negative or positive. This priority affects only the start order of +// fetchers that are being delayed by a fetcher service. +@property (assign) NSInteger servicePriority; + +// The thread used to run this fetcher in the fetcher service when no operation +// queue is provided. +@property (retain) NSThread *thread; + +// The delegate is retained during the connection +@property (retain) id delegate; + +// On iOS 4 and later, the fetch may optionally continue while the app is in the +// background until finished or stopped by OS expiration +// +// The default value is NO +// +// For Mac OS X, background fetches are always supported, and this property +// is ignored +@property (assign) BOOL shouldFetchInBackground; + +// The delegate's optional sentData selector may be used to monitor upload +// progress. It should have a signature like: +// - (void)myFetcher:(GTMHTTPFetcher *)fetcher +// didSendBytes:(NSInteger)bytesSent +// totalBytesSent:(NSInteger)totalBytesSent +// totalBytesExpectedToSend:(NSInteger)totalBytesExpectedToSend; +// +// +doesSupportSentDataCallback indicates if this delegate method is supported ++ (BOOL)doesSupportSentDataCallback; + +@property (assign) SEL sentDataSelector; + +// The delegate's optional receivedData selector may be used to monitor download +// progress. It should have a signature like: +// - (void)myFetcher:(GTMHTTPFetcher *)fetcher +// receivedData:(NSData *)dataReceivedSoFar; +// +// The dataReceived argument will be nil when downloading to a path or to a +// file handle. +// +// Applications should not use this method to accumulate the received data; +// the callback method or block supplied to the beginFetch call will have +// the complete NSData received. +@property (assign) SEL receivedDataSelector; + +#if NS_BLOCKS_AVAILABLE +// The full interface to the block is provided rather than just a typedef for +// its parameter list in order to get more useful code completion in the Xcode +// editor +@property (copy) void (^sentDataBlock)(NSInteger bytesSent, NSInteger totalBytesSent, NSInteger bytesExpectedToSend); + +// The dataReceived argument will be nil when downloading to a path or to +// a file handle +@property (copy) void (^receivedDataBlock)(NSData *dataReceivedSoFar); +#endif + +// retrying; see comments at the top of the file. Calling +// setRetryEnabled(YES) resets the min and max retry intervals. +@property (assign, getter=isRetryEnabled) BOOL retryEnabled; + +// Retry selector or block is optional for retries. +// +// If present, it should have the signature: +// -(BOOL)fetcher:(GTMHTTPFetcher *)fetcher willRetry:(BOOL)suggestedWillRetry forError:(NSError *)error +// and return YES to cause a retry. See comments at the top of this file. +@property (assign) SEL retrySelector; + +#if NS_BLOCKS_AVAILABLE +@property (copy) BOOL (^retryBlock)(BOOL suggestedWillRetry, NSError *error); +#endif + +// Retry intervals must be strictly less than maxRetryInterval, else +// they will be limited to maxRetryInterval and no further retries will +// be attempted. Setting maxRetryInterval to 0.0 will reset it to the +// default value, 600 seconds. + +@property (assign) NSTimeInterval maxRetryInterval; + +// Starting retry interval. Setting minRetryInterval to 0.0 will reset it +// to a random value between 1.0 and 2.0 seconds. Clients should normally not +// call this except for unit testing. +@property (assign) NSTimeInterval minRetryInterval; + +// Multiplier used to increase the interval between retries, typically 2.0. +// Clients should not need to call this. +@property (assign) double retryFactor; + +// Number of retries attempted +@property (readonly) NSUInteger retryCount; + +// interval delay to precede next retry +@property (readonly) NSTimeInterval nextRetryInterval; + +// Begin fetching the request +// +// The delegate can optionally implement the finished selectors or pass NULL +// for it. +// +// Returns YES if the fetch is initiated. The delegate is retained between +// the beginFetch call until after the finish callback. +// +// An error is passed to the callback for server statuses 300 or +// higher, with the status stored as the error object's code. +// +// finishedSEL has a signature like: +// - (void)fetcher:(GTMHTTPFetcher *)fetcher finishedWithData:(NSData *)data error:(NSError *)error; +// +// If the application has specified a downloadPath or downloadFileHandle +// for the fetcher, the data parameter passed to the callback will be nil. + +- (BOOL)beginFetchWithDelegate:(id)delegate + didFinishSelector:(SEL)finishedSEL; + +#if NS_BLOCKS_AVAILABLE +- (BOOL)beginFetchWithCompletionHandler:(void (^)(NSData *data, NSError *error))handler; +#endif + + +// Returns YES if this is in the process of fetching a URL +- (BOOL)isFetching; + +// Cancel the fetch of the request that's currently in progress +- (void)stopFetching; + +// Return the status code from the server response +@property (readonly) NSInteger statusCode; + +// Return the http headers from the response +@property (retain, readonly) NSDictionary *responseHeaders; + +// The response, once it's been received +@property (retain) NSURLResponse *response; + +// Bytes downloaded so far +@property (readonly) unsigned long long downloadedLength; + +// Buffer of currently-downloaded data +@property (readonly, retain) NSData *downloadedData; + +// Path in which to non-atomically create a file for storing the downloaded data +// +// The path must be set before fetching begins. The download file handle +// will be created for the path, and can be used to monitor progress. If a file +// already exists at the path, it will be overwritten. +@property (copy) NSString *downloadPath; + +// If downloadFileHandle is set, data received is immediately appended to +// the file handle rather than being accumulated in the downloadedData property +// +// The file handle supplied must allow writing and support seekToFileOffset:, +// and must be set before fetching begins. Setting a download path will +// override the file handle property. +@property (retain) NSFileHandle *downloadFileHandle; + +// The optional fetchHistory object is used for a sequence of fetchers to +// remember ETags, cache ETagged data, and store cookies. Typically, this +// is set by a GTMFetcherService object when it creates a fetcher. +// +// Side effect: setting fetch history implicitly calls setCookieStorageMethod: +@property (retain) id fetchHistory; + +// userData is retained for the convenience of the caller +@property (retain) id userData; + +// Stored property values are retained for the convenience of the caller +@property (copy) NSMutableDictionary *properties; + +- (void)setProperty:(id)obj forKey:(NSString *)key; // pass nil obj to remove property +- (id)propertyForKey:(NSString *)key; + +- (void)addPropertiesFromDictionary:(NSDictionary *)dict; + +// Comments are useful for logging +@property (copy) NSString *comment; + +- (void)setCommentWithFormat:(NSString *)format, ... NS_FORMAT_FUNCTION(1, 2); + +// Log of request and response, if logging is enabled +@property (copy) NSString *log; + +// Callbacks can be invoked on an operation queue rather than via the run loop, +// starting on 10.7 and iOS 6. If a delegate queue is supplied. the run loop +// modes are ignored. +@property (retain) NSOperationQueue *delegateQueue; + +// Using the fetcher while a modal dialog is displayed requires setting the +// run-loop modes to include NSModalPanelRunLoopMode +@property (retain) NSArray *runLoopModes; + +// Users who wish to replace GTMHTTPFetcher's use of NSURLConnection +// can do so globally here. The replacement should be a subclass of +// NSURLConnection. ++ (Class)connectionClass; ++ (void)setConnectionClass:(Class)theClass; + +// Spin the run loop, discarding events, until the fetch has completed +// +// This is only for use in testing or in tools without a user interface. +// +// Synchronous fetches should never be done by shipping apps; they are +// sufficient reason for rejection from the app store. +- (void)waitForCompletionWithTimeout:(NSTimeInterval)timeoutInSeconds; + +#if STRIP_GTM_FETCH_LOGGING +// if logging is stripped, provide a stub for the main method +// for controlling logging ++ (void)setLoggingEnabled:(BOOL)flag; +#endif // STRIP_GTM_FETCH_LOGGING + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTMHTTPFetcher.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTMHTTPFetcher.m new file mode 100644 index 0000000000..a536783195 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTMHTTPFetcher.m @@ -0,0 +1,1935 @@ +/* Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTMHTTPFetcher.m +// + +#define GTMHTTPFETCHER_DEFINE_GLOBALS 1 + +#import "GTMHTTPFetcher.h" + +#if GTM_BACKGROUND_FETCHING +#import +#endif + +static id gGTMFetcherStaticCookieStorage = nil; +static Class gGTMFetcherConnectionClass = nil; + +// The default max retry interview is 10 minutes for uploads (POST/PUT/PATCH), +// 1 minute for downloads. +static const NSTimeInterval kUnsetMaxRetryInterval = -1; +static const NSTimeInterval kDefaultMaxDownloadRetryInterval = 60.0; +static const NSTimeInterval kDefaultMaxUploadRetryInterval = 60.0 * 10.; + +// delegateQueue callback parameters +static NSString *const kCallbackData = @"data"; +static NSString *const kCallbackError = @"error"; + +// +// GTMHTTPFetcher +// + +@interface GTMHTTPFetcher () + +@property (copy) NSString *temporaryDownloadPath; +@property (retain) id cookieStorage; +@property (readwrite, retain) NSData *downloadedData; +#if NS_BLOCKS_AVAILABLE +@property (copy) void (^completionBlock)(NSData *, NSError *); +#endif + +- (BOOL)beginFetchMayDelay:(BOOL)mayDelay + mayAuthorize:(BOOL)mayAuthorize; +- (void)failToBeginFetchWithError:(NSError *)error; +- (void)failToBeginFetchDeferWithError:(NSError *)error; + +#if GTM_BACKGROUND_FETCHING +- (void)endBackgroundTask; +- (void)backgroundFetchExpired; +#endif + +- (BOOL)authorizeRequest; +- (void)authorizer:(id )auth + request:(NSMutableURLRequest *)request + finishedWithError:(NSError *)error; + +- (NSString *)createTempDownloadFilePathForPath:(NSString *)targetPath; +- (void)stopFetchReleasingCallbacks:(BOOL)shouldReleaseCallbacks; +- (BOOL)shouldReleaseCallbacksUponCompletion; + +- (void)addCookiesToRequest:(NSMutableURLRequest *)request; +- (void)handleCookiesForResponse:(NSURLResponse *)response; + +- (void)invokeFetchCallbacksWithData:(NSData *)data + error:(NSError *)error; +- (void)invokeFetchCallback:(SEL)sel + target:(id)target + data:(NSData *)data + error:(NSError *)error; +- (void)invokeFetchCallbacksOnDelegateQueueWithData:(NSData *)data + error:(NSError *)error; +- (void)releaseCallbacks; + +- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error; + +- (BOOL)shouldRetryNowForStatus:(NSInteger)status error:(NSError *)error; +- (void)destroyRetryTimer; +- (void)beginRetryTimer; +- (void)primeRetryTimerWithNewTimeInterval:(NSTimeInterval)secs; +- (void)sendStopNotificationIfNeeded; +- (void)retryFetch; +- (void)retryTimerFired:(NSTimer *)timer; +@end + +@interface GTMHTTPFetcher (GTMHTTPFetcherLoggingInternal) +- (void)setupStreamLogging; +- (void)logFetchWithError:(NSError *)error; +@end + +@implementation GTMHTTPFetcher + ++ (GTMHTTPFetcher *)fetcherWithRequest:(NSURLRequest *)request { + return [[[[self class] alloc] initWithRequest:request] autorelease]; +} + ++ (GTMHTTPFetcher *)fetcherWithURL:(NSURL *)requestURL { + return [self fetcherWithRequest:[NSURLRequest requestWithURL:requestURL]]; +} + ++ (GTMHTTPFetcher *)fetcherWithURLString:(NSString *)requestURLString { + return [self fetcherWithURL:[NSURL URLWithString:requestURLString]]; +} + ++ (void)initialize { + // initialize is guaranteed by the runtime to be called in a + // thread-safe manner + if (!gGTMFetcherStaticCookieStorage) { + Class cookieStorageClass = NSClassFromString(@"GTMCookieStorage"); + if (cookieStorageClass) { + gGTMFetcherStaticCookieStorage = [[cookieStorageClass alloc] init]; + } + } +} + +- (id)init { + return [self initWithRequest:nil]; +} + +- (id)initWithRequest:(NSURLRequest *)request { + self = [super init]; + if (self) { + request_ = [request mutableCopy]; + + if (gGTMFetcherStaticCookieStorage != nil) { + // The user has compiled with the cookie storage class available; + // default to static cookie storage, so our cookies are independent + // of the cookies of other apps. + [self setCookieStorageMethod:kGTMHTTPFetcherCookieStorageMethodStatic]; + } else { + // Default to system default cookie storage + [self setCookieStorageMethod:kGTMHTTPFetcherCookieStorageMethodSystemDefault]; + } + } + return self; +} + +- (id)copyWithZone:(NSZone *)zone { + // disallow use of fetchers in a copy property + [self doesNotRecognizeSelector:_cmd]; + return nil; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"%@ %p (%@)", + [self class], self, [self.mutableRequest URL]]; +} + +#if !GTM_IPHONE +- (void)finalize { + [self stopFetchReleasingCallbacks:YES]; // releases connection_, destroys timers + [super finalize]; +} +#endif + +- (void)dealloc { +#if DEBUG + NSAssert(!isStopNotificationNeeded_, + @"unbalanced fetcher notification for %@", [request_ URL]); +#endif + + // Note: if a connection or a retry timer was pending, then this instance + // would be retained by those so it wouldn't be getting dealloc'd, + // hence we don't need to stopFetch here + [request_ release]; + [connection_ release]; + [downloadedData_ release]; + [downloadPath_ release]; + [temporaryDownloadPath_ release]; + [downloadFileHandle_ release]; + [credential_ release]; + [proxyCredential_ release]; + [postData_ release]; + [postStream_ release]; + [loggedStreamData_ release]; + [response_ release]; +#if NS_BLOCKS_AVAILABLE + [completionBlock_ release]; + [receivedDataBlock_ release]; + [sentDataBlock_ release]; + [retryBlock_ release]; +#endif + [userData_ release]; + [properties_ release]; + [delegateQueue_ release]; + [runLoopModes_ release]; + [fetchHistory_ release]; + [cookieStorage_ release]; + [authorizer_ release]; + [service_ release]; + [serviceHost_ release]; + [thread_ release]; + [retryTimer_ release]; + [comment_ release]; + [log_ release]; +#if !STRIP_GTM_FETCH_LOGGING + [logRequestBody_ release]; + [logResponseBody_ release]; +#endif + + [super dealloc]; +} + +#pragma mark - + +// Begin fetching the URL (or begin a retry fetch). The delegate is retained +// for the duration of the fetch connection. + +- (BOOL)beginFetchWithDelegate:(id)delegate + didFinishSelector:(SEL)finishedSelector { + GTMAssertSelectorNilOrImplementedWithArgs(delegate, finishedSelector, @encode(GTMHTTPFetcher *), @encode(NSData *), @encode(NSError *), 0); + GTMAssertSelectorNilOrImplementedWithArgs(delegate, receivedDataSel_, @encode(GTMHTTPFetcher *), @encode(NSData *), 0); + GTMAssertSelectorNilOrImplementedWithArgs(delegate, retrySel_, @encode(GTMHTTPFetcher *), @encode(BOOL), @encode(NSError *), 0); + + // We'll retain the delegate only during the outstanding connection (similar + // to what Cocoa does with performSelectorOnMainThread:) and during + // authorization or delays, since the app would crash + // if the delegate was released before the fetch calls back + [self setDelegate:delegate]; + finishedSel_ = finishedSelector; + + return [self beginFetchMayDelay:YES + mayAuthorize:YES]; +} + +- (BOOL)beginFetchMayDelay:(BOOL)mayDelay + mayAuthorize:(BOOL)mayAuthorize { + // This is the internal entry point for re-starting fetches + NSError *error = nil; + + if (connection_ != nil) { + NSAssert1(connection_ != nil, @"fetch object %@ being reused; this should never happen", self); + goto CannotBeginFetch; + } + + if (request_ == nil || [request_ URL] == nil) { + NSAssert(request_ != nil, @"beginFetchWithDelegate requires a request with a URL"); + goto CannotBeginFetch; + } + + self.downloadedData = nil; + downloadedLength_ = 0; + + if (mayDelay && service_) { + BOOL shouldFetchNow = [service_ fetcherShouldBeginFetching:self]; + if (!shouldFetchNow) { + // the fetch is deferred, but will happen later + return YES; + } + } + + NSString *effectiveHTTPMethod = [request_ valueForHTTPHeaderField:@"X-HTTP-Method-Override"]; + if (effectiveHTTPMethod == nil) { + effectiveHTTPMethod = [request_ HTTPMethod]; + } + BOOL isEffectiveHTTPGet = (effectiveHTTPMethod == nil + || [effectiveHTTPMethod isEqual:@"GET"]); + + if (postData_ || postStream_) { + if (isEffectiveHTTPGet) { + [request_ setHTTPMethod:@"POST"]; + isEffectiveHTTPGet = NO; + } + + if (postData_) { + [request_ setHTTPBody:postData_]; + } else { + if ([self respondsToSelector:@selector(setupStreamLogging)]) { + [self performSelector:@selector(setupStreamLogging)]; + } + + [request_ setHTTPBodyStream:postStream_]; + } + } + + // We authorize after setting up the http method and body in the request + // because OAuth 1 may need to sign the request body + if (mayAuthorize && authorizer_) { + BOOL isAuthorized = [authorizer_ isAuthorizedRequest:request_]; + if (!isAuthorized) { + // authorization needed + return [self authorizeRequest]; + } + } + + [fetchHistory_ updateRequest:request_ isHTTPGet:isEffectiveHTTPGet]; + + // set the default upload or download retry interval, if necessary + if (isRetryEnabled_ + && maxRetryInterval_ <= kUnsetMaxRetryInterval) { + if (isEffectiveHTTPGet || [effectiveHTTPMethod isEqual:@"HEAD"]) { + [self setMaxRetryInterval:kDefaultMaxDownloadRetryInterval]; + } else { + [self setMaxRetryInterval:kDefaultMaxUploadRetryInterval]; + } + } + + [self addCookiesToRequest:request_]; + + if (downloadPath_ != nil) { + // downloading to a path, so create a temporary file and a file handle for + // downloading + NSString *tempPath = [self createTempDownloadFilePathForPath:downloadPath_]; + + BOOL didCreate = [[NSData data] writeToFile:tempPath + options:0 + error:&error]; + if (!didCreate) goto CannotBeginFetch; + + [self setTemporaryDownloadPath:tempPath]; + + NSFileHandle *fh = [NSFileHandle fileHandleForWritingAtPath:tempPath]; + if (fh == nil) goto CannotBeginFetch; + + [self setDownloadFileHandle:fh]; + } + + // finally, start the connection + + Class connectionClass = [[self class] connectionClass]; + + NSOperationQueue *delegateQueue = delegateQueue_; + if (delegateQueue && + ![connectionClass instancesRespondToSelector:@selector(setDelegateQueue:)]) { + // NSURLConnection has no setDelegateQueue: on iOS 4 and Mac OS X 10.5. + delegateQueue = nil; + self.delegateQueue = nil; + } + +#if DEBUG && TARGET_OS_IPHONE + BOOL isPreIOS6 = (NSFoundationVersionNumber <= 890.1); + if (isPreIOS6 && delegateQueue) { + NSLog(@"GTMHTTPFetcher delegateQueue not safe in iOS 5"); + } +#endif + + 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 + connection_ = [[connectionClass connectionWithRequest:request_ + delegate:self] retain]; + } else { + // Specify callbacks be on an operation queue or on the current run loop + // in the specified modes + connection_ = [[connectionClass alloc] initWithRequest:request_ + delegate:self + startImmediately:NO]; + if (delegateQueue) { + [connection_ performSelector:@selector(setDelegateQueue:) + withObject:delegateQueue]; + } else if (runLoopModes_) { + NSRunLoop *rl = [NSRunLoop currentRunLoop]; + for (NSString *mode in runLoopModes_) { + [connection_ scheduleInRunLoop:rl forMode:mode]; + } + } + [connection_ start]; + } + hasConnectionEnded_ = NO; + + if (!connection_) { + NSAssert(connection_ != nil, @"beginFetchWithDelegate could not create a connection"); + 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_) { + // For iOS 3 compatibility, ensure that UIApp supports backgrounding + UIApplication *app = [UIApplication sharedApplication]; + if ([app respondsToSelector:@selector(beginBackgroundTaskWithExpirationHandler:)]) { + // Tell UIApplication that we want to continue even when the app is in the + // background. + NSThread *thread = [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]; + }]; + } + } +#endif + + // Once connection_ is non-nil we can send the start notification + isStopNotificationNeeded_ = YES; + NSNotificationCenter *defaultNC = [NSNotificationCenter defaultCenter]; + [defaultNC postNotificationName:kGTMHTTPFetcherStartedNotification + object:self]; + return YES; + +CannotBeginFetch: + [self failToBeginFetchDeferWithError:error]; + return NO; +} + +- (void)failToBeginFetchDeferWithError:(NSError *)error { + if (delegateQueue_) { + // Deferring will happen by the callback being invoked on the specified + // queue. + [self failToBeginFetchWithError:error]; + } else { + // No delegate queue has been specified, so put the callback + // on an appropriate run loop. + NSArray *modes = (runLoopModes_ ? runLoopModes_ : + [NSArray arrayWithObject:NSRunLoopCommonModes]); + [self performSelector:@selector(failToBeginFetchWithError:) + onThread:[NSThread currentThread] + withObject:error + waitUntilDone:NO + modes:modes]; + } +} + +- (void)failToBeginFetchWithError:(NSError *)error { + if (error == nil) { + error = [NSError errorWithDomain:kGTMHTTPFetcherErrorDomain + code:kGTMHTTPFetcherErrorDownloadFailed + userInfo:nil]; + } + + [[self retain] autorelease]; // In case the callback releases us + + [self invokeFetchCallbacksOnDelegateQueueWithData:nil + error:error]; + + [self releaseCallbacks]; + + [service_ fetcherDidStop:self]; + + self.authorizer = nil; + + if (temporaryDownloadPath_) { + [[NSFileManager defaultManager] removeItemAtPath:temporaryDownloadPath_ + error:NULL]; + self.temporaryDownloadPath = nil; + } +} + +#if GTM_BACKGROUND_FETCHING +- (void)backgroundFetchExpired { + // On background expiration, we stop the fetch and invoke the callbacks + NSError *error = [NSError errorWithDomain:kGTMHTTPFetcherErrorDomain + code:kGTMHTTPFetcherErrorBackgroundExpiration + userInfo:nil]; + [self invokeFetchCallbacksOnDelegateQueueWithData:nil + error:error]; + @synchronized(self) { + // Stopping the fetch here will indirectly call endBackgroundTask + [self stopFetchReleasingCallbacks:NO]; + + [self releaseCallbacks]; + self.authorizer = nil; + } +} + +- (void)endBackgroundTask { + @synchronized(self) { + // Whenever the connection stops or background execution expires, + // we need to tell UIApplication we're done + if (backgroundTaskIdentifer_) { + // If backgroundTaskIdentifer_ is non-zero, we know we're on iOS 4 + UIApplication *app = [UIApplication sharedApplication]; + [app endBackgroundTask:backgroundTaskIdentifer_]; + + backgroundTaskIdentifer_ = 0; + } + } +} +#endif // GTM_BACKGROUND_FETCHING + +- (BOOL)authorizeRequest { + id authorizer = self.authorizer; + SEL asyncAuthSel = @selector(authorizeRequest:delegate:didFinishSelector:); + if ([authorizer respondsToSelector:asyncAuthSel]) { + SEL callbackSel = @selector(authorizer:request:finishedWithError:); + [authorizer authorizeRequest:request_ + delegate:self + didFinishSelector:callbackSel]; + return YES; + } else { + NSAssert(authorizer == nil, @"invalid authorizer for fetch"); + + // No authorizing possible, and authorizing happens only after any delay; + // just begin fetching + return [self beginFetchMayDelay:NO + mayAuthorize:NO]; + } +} + +- (void)authorizer:(id )auth + request:(NSMutableURLRequest *)request + finishedWithError:(NSError *)error { + if (error != nil) { + // We can't fetch without authorization + [self failToBeginFetchDeferWithError:error]; + } else { + [self beginFetchMayDelay:NO + mayAuthorize:NO]; + } +} + +#if NS_BLOCKS_AVAILABLE +- (BOOL)beginFetchWithCompletionHandler:(void (^)(NSData *data, NSError *error))handler { + self.completionBlock = handler; + + // The user may have called setDelegate: earlier if they want to use other + // delegate-style callbacks during the fetch; otherwise, the delegate is nil, + // which is fine. + return [self beginFetchWithDelegate:[self delegate] + didFinishSelector:nil]; +} +#endif + +- (NSString *)createTempDownloadFilePathForPath:(NSString *)targetPath { + NSString *tempDir = nil; + +#if (!TARGET_OS_IPHONE && (MAC_OS_X_VERSION_MAX_ALLOWED >= 1060)) + // Find an appropriate directory for the download, ideally on the same disk + // as the final target location so the temporary file won't have to be moved + // to a different disk. + // + // Available in SDKs for 10.6 and iOS 4 + // + // Oct 2011: We previously also used URLForDirectory for + // (TARGET_OS_IPHONE && (__IPHONE_OS_VERSION_MAX_ALLOWED >= 40000)) + // but that is returning a non-temporary directory for iOS, unfortunately + + SEL sel = @selector(URLForDirectory:inDomain:appropriateForURL:create:error:); + if ([NSFileManager instancesRespondToSelector:sel]) { + NSError *error = nil; + NSURL *targetURL = [NSURL fileURLWithPath:targetPath]; + NSFileManager *fileMgr = [NSFileManager defaultManager]; + + NSURL *tempDirURL = [fileMgr URLForDirectory:NSItemReplacementDirectory + inDomain:NSUserDomainMask + appropriateForURL:targetURL + create:YES + error:&error]; + tempDir = [tempDirURL path]; + } +#endif + + if (tempDir == nil) { + tempDir = NSTemporaryDirectory(); + } + + static unsigned int counter = 0; + NSString *name = [NSString stringWithFormat:@"gtmhttpfetcher_%u_%u", + ++counter, (unsigned int) arc4random()]; + NSString *result = [tempDir stringByAppendingPathComponent:name]; + return result; +} + +- (void)addCookiesToRequest:(NSMutableURLRequest *)request { + // Get cookies for this URL from our storage array, if + // we have a storage array + if (cookieStorageMethod_ != kGTMHTTPFetcherCookieStorageMethodSystemDefault + && cookieStorageMethod_ != kGTMHTTPFetcherCookieStorageMethodNone) { + + NSArray *cookies = [cookieStorage_ cookiesForURL:[request URL]]; + if ([cookies count] > 0) { + + NSDictionary *headerFields = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies]; + NSString *cookieHeader = [headerFields objectForKey:@"Cookie"]; // key used in header dictionary + if (cookieHeader) { + [request addValue:cookieHeader forHTTPHeaderField:@"Cookie"]; // header name + } + } + } +} + +// Returns YES if this is in the process of fetching a URL, or waiting to +// retry, or waiting for authorization, or waiting to be issued by the +// service object +- (BOOL)isFetching { + if (connection_ != nil || retryTimer_ != nil) return YES; + + BOOL isAuthorizing = [authorizer_ isAuthorizingRequest:request_]; + if (isAuthorizing) return YES; + + BOOL isDelayed = [service_ isDelayingFetcher:self]; + return isDelayed; +} + +// Returns the status code set in connection:didReceiveResponse: +- (NSInteger)statusCode { + + NSInteger statusCode; + + if (response_ != nil + && [response_ respondsToSelector:@selector(statusCode)]) { + + statusCode = [(NSHTTPURLResponse *)response_ statusCode]; + } else { + // Default to zero, in hopes of hinting "Unknown" (we can't be + // sure that things are OK enough to use 200). + statusCode = 0; + } + return statusCode; +} + +- (NSDictionary *)responseHeaders { + if (response_ != nil + && [response_ respondsToSelector:@selector(allHeaderFields)]) { + + NSDictionary *headers = [(NSHTTPURLResponse *)response_ allHeaderFields]; + return headers; + } + return nil; +} + +- (void)releaseCallbacks { + [delegate_ autorelease]; + delegate_ = nil; + + [delegateQueue_ autorelease]; + delegateQueue_ = nil; + +#if NS_BLOCKS_AVAILABLE + self.completionBlock = nil; + self.sentDataBlock = nil; + self.receivedDataBlock = nil; + self.retryBlock = nil; +#endif +} + +// Cancel the fetch of the URL that's currently in progress. +- (void)stopFetchReleasingCallbacks:(BOOL)shouldReleaseCallbacks { + id service; + + // if the connection or the retry timer is all that's retaining the fetcher, + // we want to be sure this instance survives stopping at least long enough for + // the stack to unwind + [[self retain] autorelease]; + + [self destroyRetryTimer]; + + @synchronized(self) { + service = [[service_ retain] autorelease]; + + if (connection_) { + // in case cancelling the connection calls this recursively, we want + // to ensure that we'll only release the connection and delegate once, + // so first set connection_ to nil + NSURLConnection* oldConnection = connection_; + connection_ = nil; + + if (!hasConnectionEnded_) { + [oldConnection cancel]; + } + + // this may be called in a callback from the connection, so use autorelease + [oldConnection autorelease]; + } + } + + // send the stopped notification + [self sendStopNotificationIfNeeded]; + + @synchronized(self) { + [authorizer_ stopAuthorizationForRequest:request_]; + + if (shouldReleaseCallbacks) { + [self releaseCallbacks]; + + self.authorizer = nil; + } + + if (temporaryDownloadPath_) { + [[NSFileManager defaultManager] removeItemAtPath:temporaryDownloadPath_ + error:NULL]; + self.temporaryDownloadPath = nil; + } + } + + [service fetcherDidStop:self]; + +#if GTM_BACKGROUND_FETCHING + [self endBackgroundTask]; +#endif +} + +// External stop method +- (void)stopFetching { + [self stopFetchReleasingCallbacks:YES]; +} + +- (void)sendStopNotificationIfNeeded { + BOOL sendNow = NO; + @synchronized(self) { + if (isStopNotificationNeeded_) { + isStopNotificationNeeded_ = NO; + sendNow = YES; + } + } + + if (sendNow) { + NSNotificationCenter *defaultNC = [NSNotificationCenter defaultCenter]; + [defaultNC postNotificationName:kGTMHTTPFetcherStoppedNotification + object:self]; + } +} + +- (void)retryFetch { + [self stopFetchReleasingCallbacks:NO]; + + [self beginFetchWithDelegate:delegate_ + didFinishSelector:finishedSel_]; +} + +- (void)waitForCompletionWithTimeout:(NSTimeInterval)timeoutInSeconds { + NSDate* giveUpDate = [NSDate dateWithTimeIntervalSinceNow:timeoutInSeconds]; + + // Loop until the callbacks have been called and released, and until + // the connection is no longer pending, or until the timeout has expired + BOOL isMainThread = [NSThread isMainThread]; + + while ((!hasConnectionEnded_ +#if NS_BLOCKS_AVAILABLE + || completionBlock_ != nil +#endif + || delegate_ != nil) + && [giveUpDate timeIntervalSinceNow] > 0) { + + // Run the current run loop 1/1000 of a second to give the networking + // code a chance to work + if (isMainThread || delegateQueue_ == nil) { + NSDate *stopDate = [NSDate dateWithTimeIntervalSinceNow:0.001]; + [[NSRunLoop currentRunLoop] runUntilDate:stopDate]; + } else { + [NSThread sleepForTimeInterval:0.001]; + } + } +} + +#pragma mark NSURLConnection Delegate Methods + +// +// NSURLConnection Delegate Methods +// + +// This method just says "follow all redirects", which _should_ be the default behavior, +// According to file:///Developer/ADC%20Reference%20Library/documentation/Cocoa/Conceptual/URLLoadingSystem +// but the redirects were not being followed until I added this method. May be +// a bug in the NSURLConnection code, or the documentation. +// +// In OS X 10.4.8 and earlier, the redirect request doesn't +// get the original's headers and body. This causes POSTs to fail. +// So we construct a new request, a copy of the original, with overrides from the +// redirect. +// +// Docs say that if redirectResponse is nil, just return the redirectRequest. + +- (NSURLRequest *)connection:(NSURLConnection *)connection + willSendRequest:(NSURLRequest *)redirectRequest + redirectResponse:(NSURLResponse *)redirectResponse { + @synchronized(self) { + if (redirectRequest && redirectResponse) { + // save cookies from the response + [self handleCookiesForResponse:redirectResponse]; + + NSMutableURLRequest *newRequest = [[request_ mutableCopy] autorelease]; + // copy the URL + NSURL *redirectURL = [redirectRequest URL]; + NSURL *url = [newRequest URL]; + + // disallow scheme changes (say, from https to http) + NSString *redirectScheme = [url scheme]; + NSString *newScheme = [redirectURL scheme]; + NSString *newResourceSpecifier = [redirectURL resourceSpecifier]; + + if ([redirectScheme caseInsensitiveCompare:@"http"] == NSOrderedSame + && newScheme != nil + && [newScheme caseInsensitiveCompare:@"https"] == NSOrderedSame) { + + // allow the change from http to https + redirectScheme = newScheme; + } + + NSString *newUrlString = [NSString stringWithFormat:@"%@:%@", + redirectScheme, newResourceSpecifier]; + + NSURL *newURL = [NSURL URLWithString:newUrlString]; + [newRequest setURL:newURL]; + + // any headers in the redirect override headers in the original. + NSDictionary *redirectHeaders = [redirectRequest allHTTPHeaderFields]; + for (NSString *key in redirectHeaders) { + NSString *value = [redirectHeaders objectForKey:key]; + [newRequest setValue:value forHTTPHeaderField:key]; + } + + [self addCookiesToRequest:newRequest]; + + redirectRequest = newRequest; + + // log the response we just received + [self setResponse:redirectResponse]; + [self logNowWithError:nil]; + + // update the request for future logging + NSMutableURLRequest *mutable = [[redirectRequest mutableCopy] autorelease]; + [self setMutableRequest:mutable]; + } + return redirectRequest; + } +} + +- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { + @synchronized(self) { + // This method is called when the server has determined that it + // has enough information to create the NSURLResponse + // it can be called multiple times, for example in the case of a + // redirect, so each time we reset the data. + [downloadedData_ setLength:0]; + [downloadFileHandle_ truncateFileAtOffset:0]; + downloadedLength_ = 0; + + [self setResponse:response]; + + // Save cookies from the response + [self handleCookiesForResponse:response]; + } +} + + +// handleCookiesForResponse: handles storage of cookies for responses passed to +// connection:willSendRequest:redirectResponse: and connection:didReceiveResponse: +- (void)handleCookiesForResponse:(NSURLResponse *)response { + + if (cookieStorageMethod_ == kGTMHTTPFetcherCookieStorageMethodSystemDefault + || cookieStorageMethod_ == kGTMHTTPFetcherCookieStorageMethodNone) { + + // do nothing special for NSURLConnection's default storage mechanism + // or when we're ignoring cookies + + } else if ([response respondsToSelector:@selector(allHeaderFields)]) { + + // grab the cookies from the header as NSHTTPCookies and store them either + // into our static array or into the fetchHistory + + NSDictionary *responseHeaderFields = [(NSHTTPURLResponse *)response allHeaderFields]; + if (responseHeaderFields) { + + NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:responseHeaderFields + forURL:[response URL]]; + if ([cookies count] > 0) { + [cookieStorage_ setCookies:cookies]; + } + } + } +} + +-(void)connection:(NSURLConnection *)connection +didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { + @synchronized(self) { + if ([challenge previousFailureCount] <= 2) { + + NSURLCredential *credential = credential_; + + if ([[challenge protectionSpace] isProxy] && proxyCredential_ != nil) { + credential = proxyCredential_; + } + + // Here, if credential is still nil, then we *could* try to get it from + // NSURLCredentialStorage's defaultCredentialForProtectionSpace:. + // We don't, because we're assuming: + // + // - for server credentials, we only want ones supplied by the program + // calling http fetcher + // - for proxy credentials, if one were necessary and available in the + // keychain, it would've been found automatically by NSURLConnection + // and this challenge delegate method never would've been called + // anyway + + if (credential) { + // try the credential + [[challenge sender] useCredential:credential + forAuthenticationChallenge:challenge]; + return; + } + } + + // 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 + // dictionary. +#if DEBUG + NSAssert(!isCancellingChallenge_, @"isCancellingChallenge_ unexpected"); +#endif + NSDictionary *userInfo = [NSDictionary dictionaryWithObject:challenge + forKey:kGTMHTTPFetcherErrorChallengeKey]; + NSError *error = [NSError errorWithDomain:kGTMHTTPFetcherErrorDomain + code:kGTMHTTPFetcherErrorAuthenticationChallengeFailed + userInfo:userInfo]; + + // cancelAuthenticationChallenge seems to indirectly call + // connection:didFailWithError: now, though that isn't documented + // + // We'll use an ivar to make the indirect invocation of the + // delegate method do nothing. + isCancellingChallenge_ = YES; + [[challenge sender] cancelAuthenticationChallenge:challenge]; + isCancellingChallenge_ = NO; + + [self connection:connection didFailWithError:error]; + } +} + +- (void)invokeFetchCallbacksWithData:(NSData *)data + error:(NSError *)error { + // To avoid deadlocks, this should not be called inside of @synchronized(self) + id target; + SEL sel; +#if NS_BLOCKS_AVAILABLE + void (^block)(NSData *, NSError *); +#endif + @synchronized(self) { + target = delegate_; + sel = finishedSel_; + block = completionBlock_; + } + + [[self retain] autorelease]; // In case the callback releases us + + [self invokeFetchCallback:sel + target:target + data:data + error:error]; + +#if NS_BLOCKS_AVAILABLE + if (block) { + block(data, error); + } +#endif +} + +- (void)invokeFetchCallback:(SEL)sel + target:(id)target + data:(NSData *)data + error:(NSError *)error { + // This method is available to subclasses which may provide a customized + // target pointer. + if (target && sel) { + NSMethodSignature *sig = [target methodSignatureForSelector:sel]; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig]; + [invocation setSelector:sel]; + [invocation setTarget:target]; + [invocation setArgument:&self atIndex:2]; + [invocation setArgument:&data atIndex:3]; + [invocation setArgument:&error atIndex:4]; + [invocation invoke]; + } +} + +- (void)invokeFetchCallbacksOnDelegateQueueWithData:(NSData *)data + error:(NSError *)error { + // This is called by methods that are not already on the delegateQueue + // (as NSURLConnection callbacks should already be, but other failures + // are not.) + if (!delegateQueue_) { + [self invokeFetchCallbacksWithData:data error:error]; + } + + // Values may be nil. + NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:2]; + [dict setValue:data forKey:kCallbackData]; + [dict setValue:error forKey:kCallbackError]; + NSInvocationOperation *op = + [[[NSInvocationOperation alloc] initWithTarget:self + selector:@selector(invokeOnQueueWithDictionary:) + object:dict] autorelease]; + [delegateQueue_ addOperation:op]; +} + +- (void)invokeOnQueueWithDictionary:(NSDictionary *)dict { + NSData *data = [dict objectForKey:kCallbackData]; + NSError *error = [dict objectForKey:kCallbackError]; + + [self invokeFetchCallbacksWithData:data error:error]; +} + + +- (void)invokeSentDataCallback:(SEL)sel + target:(id)target + didSendBodyData:(NSInteger)bytesWritten + totalBytesWritten:(NSInteger)totalBytesWritten + totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite { + if (target && sel) { + NSMethodSignature *sig = [target methodSignatureForSelector:sel]; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig]; + [invocation setSelector:sel]; + [invocation setTarget:target]; + [invocation setArgument:&self atIndex:2]; + [invocation setArgument:&bytesWritten atIndex:3]; + [invocation setArgument:&totalBytesWritten atIndex:4]; + [invocation setArgument:&totalBytesExpectedToWrite atIndex:5]; + [invocation invoke]; + } +} + +- (BOOL)invokeRetryCallback:(SEL)sel + target:(id)target + willRetry:(BOOL)willRetry + error:(NSError *)error { + if (target && sel) { + NSMethodSignature *sig = [target methodSignatureForSelector:sel]; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig]; + [invocation setSelector:sel]; + [invocation setTarget:target]; + [invocation setArgument:&self atIndex:2]; + [invocation setArgument:&willRetry atIndex:3]; + [invocation setArgument:&error atIndex:4]; + [invocation invoke]; + + [invocation getReturnValue:&willRetry]; + } + return willRetry; +} + +- (void)connection:(NSURLConnection *)connection + didSendBodyData:(NSInteger)bytesWritten + totalBytesWritten:(NSInteger)totalBytesWritten +totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite { + @synchronized(self) { + SEL sel = [self sentDataSelector]; + [self invokeSentDataCallback:sel + target:delegate_ + didSendBodyData:bytesWritten + totalBytesWritten:totalBytesWritten + totalBytesExpectedToWrite:totalBytesExpectedToWrite]; + +#if NS_BLOCKS_AVAILABLE + if (sentDataBlock_) { + sentDataBlock_(bytesWritten, totalBytesWritten, totalBytesExpectedToWrite); + } +#endif + } +} + +- (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((downloadFileHandle_ == nil) != (downloadedData_ == nil), + @"received data accumulates as NSData or NSFileHandle, not both"); +#endif + + if (downloadFileHandle_ != nil) { + // Append to file + @try { + [downloadFileHandle_ writeData:data]; + + downloadedLength_ = [downloadFileHandle_ offsetInFile]; + } + @catch (NSException *exc) { + // Couldn't write to file, probably due to a full disk + NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[exc reason] + forKey:NSLocalizedDescriptionKey]; + NSError *error = [NSError errorWithDomain:kGTMHTTPFetcherStatusDomain + code:kGTMHTTPFetcherErrorFileHandleException + userInfo:userInfo]; + [self connection:connection didFailWithError:error]; + return; + } + } else { + // append to mutable data + [downloadedData_ appendData:data]; + + downloadedLength_ = [downloadedData_ length]; + } + + if (receivedDataSel_) { + [delegate_ performSelector:receivedDataSel_ + withObject:self + withObject:downloadedData_]; + } + +#if NS_BLOCKS_AVAILABLE + if (receivedDataBlock_) { + receivedDataBlock_(downloadedData_); + } +#endif + } +} + +// For error 304's ("Not Modified") where we've cached the data, return +// status 200 ("OK") to the caller (but leave the fetcher status as 304) +// and copy the cached data. +// +// For other errors or if there's no cached data, just return the actual status. +- (NSData *)cachedDataForStatus { + if ([self statusCode] == kGTMHTTPFetcherStatusNotModified + && [fetchHistory_ shouldCacheETaggedData]) { + NSData *cachedData = [fetchHistory_ cachedDataForRequest:request_]; + return cachedData; + } + return nil; +} + +- (NSInteger)statusAfterHandlingNotModifiedError { + NSInteger status = [self statusCode]; + NSData *cachedData = [self cachedDataForStatus]; + if (cachedData) { + // Forge the status to pass on to the delegate + status = 200; + + // Copy our stored data + if (downloadFileHandle_ != nil) { + @try { + // Downloading to a file handle won't save to the cache (the data is + // likely inappropriately large for caching), but will still read from + // the cache, on the unlikely chance that the response was Not Modified + // and the URL response was indeed present in the cache. + [downloadFileHandle_ truncateFileAtOffset:0]; + [downloadFileHandle_ writeData:cachedData]; + downloadedLength_ = [downloadFileHandle_ offsetInFile]; + } + @catch (NSException *) { + // Failed to write data, likely due to lack of disk space + status = kGTMHTTPFetcherErrorFileHandleException; + } + } else { + [downloadedData_ setData:cachedData]; + downloadedLength_ = [cachedData length]; + } + } + return status; +} + +- (void)connectionDidFinishLoading:(NSURLConnection *)connection { + BOOL shouldStopFetching = YES; + BOOL shouldSendStopNotification = NO; + NSError *error = nil; + NSData *downloadedData; +#if !STRIP_GTM_FETCH_LOGGING + BOOL shouldDeferLogging = NO; +#endif + BOOL shouldBeginRetryTimer = NO; + BOOL hasLogged = NO; + + @synchronized(self) { + // We no longer need to cancel the connection + hasConnectionEnded_ = YES; + + // Skip caching ETagged results when the data is being saved to a file + if (downloadFileHandle_ == nil) { + [fetchHistory_ updateFetchHistoryWithRequest:request_ + response:response_ + downloadedData:downloadedData_]; + } else { + [fetchHistory_ removeCachedDataForRequest:request_]; + } + + [[self retain] autorelease]; // in case the callback releases us + + NSInteger status = [self statusCode]; + if ([self cachedDataForStatus] != nil) { + // Log the pre-cache response. + [self logNowWithError:nil]; + hasLogged = YES; + status = [self statusAfterHandlingNotModifiedError]; + } + + shouldSendStopNotification = YES; + + if (status >= 0 && status < 300) { + // success + if (downloadPath_) { + // Avoid deleting the downloaded file when the fetch stops + [downloadFileHandle_ closeFile]; + self.downloadFileHandle = nil; + + NSFileManager *fileMgr = [NSFileManager defaultManager]; + [fileMgr removeItemAtPath:downloadPath_ + error:NULL]; + + if ([fileMgr moveItemAtPath:temporaryDownloadPath_ + toPath:downloadPath_ + error:&error]) { + self.temporaryDownloadPath = nil; + } + } + } else { + // unsuccessful + if (!hasLogged) { + [self logNowWithError:nil]; + hasLogged = YES; + } + // Status over 300; retry or notify the delegate of failure + if ([self shouldRetryNowForStatus:status error:nil]) { + // retrying + shouldBeginRetryTimer = YES; + shouldStopFetching = NO; + } else { + NSDictionary *userInfo = nil; + if ([downloadedData_ length] > 0) { + userInfo = [NSDictionary dictionaryWithObject:downloadedData_ + forKey:kGTMHTTPFetcherStatusDataKey]; + } + error = [NSError errorWithDomain:kGTMHTTPFetcherStatusDomain + code:status + userInfo:userInfo]; + } + } + downloadedData = downloadedData_; +#if !STRIP_GTM_FETCH_LOGGING + shouldDeferLogging = shouldDeferResponseBodyLogging_; +#endif + } + + if (shouldBeginRetryTimer) { + [self beginRetryTimer]; + } + + if (shouldSendStopNotification) { + // We want to send the stop notification before calling the delegate's + // callback selector, since the callback selector may release all of + // the fetcher properties that the client is using to track the fetches. + // + // We'll also stop now so that, to any observers watching the notifications, + // it doesn't look like our wait for a retry (which may be long, + // 30 seconds or more) is part of the network activity. + [self sendStopNotificationIfNeeded]; + } + + if (shouldStopFetching) { + // Call the callbacks (outside of the @synchronized to avoid deadlocks.) + [self invokeFetchCallbacksWithData:downloadedData + error:error]; + BOOL shouldRelease = [self shouldReleaseCallbacksUponCompletion]; + [self stopFetchReleasingCallbacks:shouldRelease]; + } + + @synchronized(self) { + BOOL shouldLogNow = !hasLogged; +#if !STRIP_GTM_FETCH_LOGGING + if (shouldDeferLogging) shouldLogNow = NO; +#endif + if (shouldLogNow) { + [self logNowWithError:nil]; + } + } +} + +- (BOOL)shouldReleaseCallbacksUponCompletion { + // A subclass can override this to keep callbacks around after the + // connection has finished successfully + return YES; +} + +- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { + @synchronized(self) { + // Prevent the failure callback from being called twice, since the stopFetch + // call below (either the explicit one at the end of this method, or the + // implicit one when the retry occurs) will release the delegate. + if (connection_ == nil) return; + + // If this method was invoked indirectly by cancellation of an authentication + // challenge, defer this until it is called again with the proper error object + if (isCancellingChallenge_) return; + + // We no longer need to cancel the connection + hasConnectionEnded_ = YES; + + [self logNowWithError:error]; + } + + // See comment about sendStopNotificationIfNeeded + // in connectionDidFinishLoading: + [self sendStopNotificationIfNeeded]; + + if ([self shouldRetryNowForStatus:0 error:error]) { + [self beginRetryTimer]; + } else { + [[self retain] autorelease]; // in case the callback releases us + + [self invokeFetchCallbacksWithData:nil + error:error]; + + [self stopFetchReleasingCallbacks:YES]; + } +} + +- (void)logNowWithError:(NSError *)error { + // If the logging category is available, then log the current request, + // response, data, and error + if ([self respondsToSelector:@selector(logFetchWithError:)]) { + [self performSelector:@selector(logFetchWithError:) withObject:error]; + } +} + +#pragma mark Retries + +- (BOOL)isRetryError:(NSError *)error { + + struct retryRecord { + NSString *const domain; + int code; + }; + + struct retryRecord retries[] = { + { kGTMHTTPFetcherStatusDomain, 408 }, // request timeout + { kGTMHTTPFetcherStatusDomain, 503 }, // service unavailable + { kGTMHTTPFetcherStatusDomain, 504 }, // request timeout + { NSURLErrorDomain, NSURLErrorTimedOut }, + { NSURLErrorDomain, NSURLErrorNetworkConnectionLost }, + { nil, 0 } + }; + + // NSError's isEqual always returns false for equal but distinct instances + // of NSError, so we have to compare the domain and code values explicitly + + for (int idx = 0; retries[idx].domain != nil; idx++) { + + if ([[error domain] isEqual:retries[idx].domain] + && [error code] == retries[idx].code) { + + return YES; + } + } + return NO; +} + + +// shouldRetryNowForStatus:error: returns YES if the user has enabled retries +// and the status or error is one that is suitable for retrying. "Suitable" +// means either the isRetryError:'s list contains the status or error, or the +// user's retrySelector: is present and returns YES when called, or the +// authorizer may be able to fix. +- (BOOL)shouldRetryNowForStatus:(NSInteger)status + error:(NSError *)error { + // Determine if a refreshed authorizer may avoid an authorization error + BOOL shouldRetryForAuthRefresh = NO; + BOOL isFirstAuthError = (authorizer_ != nil) + && !hasAttemptedAuthRefresh_ + && (status == kGTMHTTPFetcherStatusUnauthorized); // 401 + + if (isFirstAuthError) { + if ([authorizer_ respondsToSelector:@selector(primeForRefresh)]) { + BOOL hasPrimed = [authorizer_ primeForRefresh]; + if (hasPrimed) { + shouldRetryForAuthRefresh = YES; + hasAttemptedAuthRefresh_ = YES; + [request_ setValue:nil forHTTPHeaderField:@"Authorization"]; + } + } + } + + // Determine if we're doing exponential backoff retries + BOOL shouldDoIntervalRetry = [self isRetryEnabled] + && ([self nextRetryInterval] < [self maxRetryInterval]); + + BOOL willRetry = NO; + BOOL canRetry = shouldRetryForAuthRefresh || shouldDoIntervalRetry; + if (canRetry) { + // Check if this is a retryable error + if (error == nil) { + // Make an error for the status + NSDictionary *userInfo = nil; + if ([downloadedData_ length] > 0) { + userInfo = [NSDictionary dictionaryWithObject:downloadedData_ + forKey:kGTMHTTPFetcherStatusDataKey]; + } + error = [NSError errorWithDomain:kGTMHTTPFetcherStatusDomain + code:status + userInfo:userInfo]; + } + + willRetry = shouldRetryForAuthRefresh || [self isRetryError:error]; + + // If the user has installed a retry callback, consult that + willRetry = [self invokeRetryCallback:retrySel_ + target:delegate_ + willRetry:willRetry + error:error]; +#if NS_BLOCKS_AVAILABLE + if (retryBlock_) { + willRetry = retryBlock_(willRetry, error); + } +#endif + } + return willRetry; +} + +- (void)beginRetryTimer { + @synchronized(self) { + if (delegateQueue_ != nil && ![NSThread isMainThread]) { + // A delegate queue is set, so the thread we're running on may not + // have a run loop. We'll defer creating and starting the timer + // until we're on the main thread to ensure it has a run loop. + // (If we weren't supporting 10.5, we could use dispatch_after instead + // of an NSTimer.) + [self performSelectorOnMainThread:_cmd + withObject:nil + waitUntilDone:NO]; + return; + } + } + + NSTimeInterval nextInterval = [self nextRetryInterval]; + NSTimeInterval maxInterval = [self maxRetryInterval]; + NSTimeInterval newInterval = MIN(nextInterval, maxInterval); + + [self primeRetryTimerWithNewTimeInterval:newInterval]; + + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc postNotificationName:kGTMHTTPFetcherRetryDelayStartedNotification + object:self]; +} + +- (void)primeRetryTimerWithNewTimeInterval:(NSTimeInterval)secs { + + [self destroyRetryTimer]; + + @synchronized(self) { + lastRetryInterval_ = secs; + + retryTimer_ = [NSTimer timerWithTimeInterval:secs + target:self + selector:@selector(retryTimerFired:) + userInfo:nil + repeats:NO]; + [retryTimer_ retain]; + + NSRunLoop *timerRL = (self.delegateQueue ? + [NSRunLoop mainRunLoop] : [NSRunLoop currentRunLoop]); + [timerRL addTimer:retryTimer_ + forMode:NSDefaultRunLoopMode]; + } +} + +- (void)retryTimerFired:(NSTimer *)timer { + [self destroyRetryTimer]; + + @synchronized(self) { + retryCount_++; + + [self retryFetch]; + } +} + +- (void)destroyRetryTimer { + BOOL shouldNotify = NO; + @synchronized(self) { + if (retryTimer_) { + [retryTimer_ invalidate]; + [retryTimer_ autorelease]; + retryTimer_ = nil; + shouldNotify = YES; + } + } + + if (shouldNotify) { + NSNotificationCenter *defaultNC = [NSNotificationCenter defaultCenter]; + [defaultNC postNotificationName:kGTMHTTPFetcherRetryDelayStoppedNotification + object:self]; + } +} + +- (NSUInteger)retryCount { + return retryCount_; +} + +- (NSTimeInterval)nextRetryInterval { + // The next wait interval is the factor (2.0) times the last interval, + // but never less than the minimum interval. + NSTimeInterval secs = lastRetryInterval_ * retryFactor_; + secs = MIN(secs, maxRetryInterval_); + secs = MAX(secs, minRetryInterval_); + + return secs; +} + +- (BOOL)isRetryEnabled { + return isRetryEnabled_; +} + +- (void)setRetryEnabled:(BOOL)flag { + + if (flag && !isRetryEnabled_) { + // We defer initializing these until the user calls setRetryEnabled + // to avoid using the random number generator if it's not needed. + // However, this means min and max intervals for this fetcher are reset + // as a side effect of calling setRetryEnabled. + // + // Make an initial retry interval random between 1.0 and 2.0 seconds + [self setMinRetryInterval:0.0]; + [self setMaxRetryInterval:kUnsetMaxRetryInterval]; + [self setRetryFactor:2.0]; + lastRetryInterval_ = 0.0; + } + isRetryEnabled_ = flag; +}; + +- (NSTimeInterval)maxRetryInterval { + return maxRetryInterval_; +} + +- (void)setMaxRetryInterval:(NSTimeInterval)secs { + if (secs > 0) { + maxRetryInterval_ = secs; + } else { + maxRetryInterval_ = kUnsetMaxRetryInterval; + } +} + +- (double)minRetryInterval { + return minRetryInterval_; +} + +- (void)setMinRetryInterval:(NSTimeInterval)secs { + if (secs > 0) { + minRetryInterval_ = secs; + } else { + // Set min interval to a random value between 1.0 and 2.0 seconds + // so that if multiple clients start retrying at the same time, they'll + // repeat at different times and avoid overloading the server + minRetryInterval_ = 1.0 + ((double)(arc4random() & 0x0FFFF) / (double) 0x0FFFF); + } +} + +#pragma mark Getters and Setters + +@dynamic cookieStorageMethod, + retryEnabled, + maxRetryInterval, + minRetryInterval, + retryCount, + nextRetryInterval, + statusCode, + responseHeaders, + fetchHistory, + userData, + properties; + +@synthesize mutableRequest = request_, + credential = credential_, + proxyCredential = proxyCredential_, + postData = postData_, + postStream = postStream_, + delegate = delegate_, + authorizer = authorizer_, + service = service_, + serviceHost = serviceHost_, + servicePriority = servicePriority_, + thread = thread_, + sentDataSelector = sentDataSel_, + receivedDataSelector = receivedDataSel_, + retrySelector = retrySel_, + retryFactor = retryFactor_, + response = response_, + downloadedLength = downloadedLength_, + downloadedData = downloadedData_, + downloadPath = downloadPath_, + temporaryDownloadPath = temporaryDownloadPath_, + downloadFileHandle = downloadFileHandle_, + delegateQueue = delegateQueue_, + runLoopModes = runLoopModes_, + comment = comment_, + log = log_, + cookieStorage = cookieStorage_; + +#if NS_BLOCKS_AVAILABLE +@synthesize completionBlock = completionBlock_, + sentDataBlock = sentDataBlock_, + receivedDataBlock = receivedDataBlock_, + retryBlock = retryBlock_; +#endif + +@synthesize shouldFetchInBackground = shouldFetchInBackground_; + +- (NSInteger)cookieStorageMethod { + return cookieStorageMethod_; +} + +- (void)setCookieStorageMethod:(NSInteger)method { + + cookieStorageMethod_ = method; + + if (method == kGTMHTTPFetcherCookieStorageMethodSystemDefault) { + // System default + [request_ setHTTPShouldHandleCookies:YES]; + + // No need for a cookie storage object + self.cookieStorage = nil; + + } else { + // Not system default + [request_ setHTTPShouldHandleCookies:NO]; + + if (method == kGTMHTTPFetcherCookieStorageMethodStatic) { + // Store cookies in the static array + NSAssert(gGTMFetcherStaticCookieStorage != nil, + @"cookie storage requires GTMHTTPFetchHistory"); + + self.cookieStorage = gGTMFetcherStaticCookieStorage; + } else if (method == kGTMHTTPFetcherCookieStorageMethodFetchHistory) { + // store cookies in the fetch history + self.cookieStorage = [fetchHistory_ cookieStorage]; + } else { + // kGTMHTTPFetcherCookieStorageMethodNone - ignore cookies + self.cookieStorage = nil; + } + } +} + ++ (id )staticCookieStorage { + return gGTMFetcherStaticCookieStorage; +} + ++ (BOOL)doesSupportSentDataCallback { +#if GTM_IPHONE + // NSURLConnection's didSendBodyData: delegate support appears to be + // available starting in iPhone OS 3.0 + return (NSFoundationVersionNumber >= 678.47); +#else + // Per WebKit's MaxFoundationVersionWithoutdidSendBodyDataDelegate + // + // Indicates if NSURLConnection will invoke the didSendBodyData: delegate + // method + return (NSFoundationVersionNumber > 677.21); +#endif +} + +- (id )fetchHistory { + return fetchHistory_; +} + +- (void)setFetchHistory:(id )fetchHistory { + [fetchHistory_ autorelease]; + fetchHistory_ = [fetchHistory retain]; + + if (fetchHistory_ != nil) { + // set the fetch history's cookie array to be the cookie store + [self setCookieStorageMethod:kGTMHTTPFetcherCookieStorageMethodFetchHistory]; + + } else { + // The fetch history was removed + if (cookieStorageMethod_ == kGTMHTTPFetcherCookieStorageMethodFetchHistory) { + // Fall back to static storage + [self setCookieStorageMethod:kGTMHTTPFetcherCookieStorageMethodStatic]; + } + } +} + +- (id)userData { + @synchronized(self) { + return userData_; + } +} + +- (void)setUserData:(id)theObj { + @synchronized(self) { + [userData_ autorelease]; + userData_ = [theObj retain]; + } +} + +- (void)setProperties:(NSMutableDictionary *)dict { + @synchronized(self) { + [properties_ autorelease]; + + // This copies rather than retains the parameter for compatiblity with + // an earlier version that took an immutable parameter and copied it. + properties_ = [dict mutableCopy]; + } +} + +- (NSMutableDictionary *)properties { + @synchronized(self) { + return properties_; + } +} + +- (void)setProperty:(id)obj forKey:(NSString *)key { + @synchronized(self) { + if (properties_ == nil && obj != nil) { + [self setProperties:[NSMutableDictionary dictionary]]; + } + [properties_ setValue:obj forKey:key]; + } +} + +- (id)propertyForKey:(NSString *)key { + @synchronized(self) { + return [properties_ objectForKey:key]; + } +} + +- (void)addPropertiesFromDictionary:(NSDictionary *)dict { + @synchronized(self) { + if (properties_ == nil && dict != nil) { + [self setProperties:[[dict mutableCopy] autorelease]]; + } else { + [properties_ addEntriesFromDictionary:dict]; + } + } +} + +- (void)setCommentWithFormat:(id)format, ... { +#if !STRIP_GTM_FETCH_LOGGING + NSString *result = format; + if (format) { + va_list argList; + va_start(argList, format); + + result = [[[NSString alloc] initWithFormat:format + arguments:argList] autorelease]; + va_end(argList); + } + [self setComment:result]; +#endif +} + ++ (Class)connectionClass { + if (gGTMFetcherConnectionClass == nil) { + gGTMFetcherConnectionClass = [NSURLConnection class]; + } + return gGTMFetcherConnectionClass; +} + ++ (void)setConnectionClass:(Class)theClass { + gGTMFetcherConnectionClass = theClass; +} + +#if STRIP_GTM_FETCH_LOGGING ++ (void)setLoggingEnabled:(BOOL)flag { +} +#endif // STRIP_GTM_FETCH_LOGGING + +@end + +void GTMAssertSelectorNilOrImplementedWithArgs(id obj, SEL sel, ...) { + + // Verify that the object's selector is implemented with the proper + // number and type of arguments +#if DEBUG + va_list argList; + va_start(argList, sel); + + if (obj && sel) { + // Check that the selector is implemented + if (![obj respondsToSelector:sel]) { + NSLog(@"\"%@\" selector \"%@\" is unimplemented or misnamed", + NSStringFromClass([obj class]), + NSStringFromSelector(sel)); + NSCAssert(0, @"callback selector unimplemented or misnamed"); + } else { + const char *expectedArgType; + unsigned int argCount = 2; // skip self and _cmd + NSMethodSignature *sig = [obj methodSignatureForSelector:sel]; + + // Check that each expected argument is present and of the correct type + while ((expectedArgType = va_arg(argList, const char*)) != 0) { + + if ([sig numberOfArguments] > argCount) { + const char *foundArgType = [sig getArgumentTypeAtIndex:argCount]; + + if(0 != strncmp(foundArgType, expectedArgType, strlen(expectedArgType))) { + NSLog(@"\"%@\" selector \"%@\" argument %d should be type %s", + NSStringFromClass([obj class]), + NSStringFromSelector(sel), (argCount - 2), expectedArgType); + NSCAssert(0, @"callback selector argument type mistake"); + } + } + argCount++; + } + + // Check that the proper number of arguments are present in the selector + if (argCount != [sig numberOfArguments]) { + NSLog( @"\"%@\" selector \"%@\" should have %d arguments", + NSStringFromClass([obj class]), + NSStringFromSelector(sel), (argCount - 2)); + NSCAssert(0, @"callback selector arguments incorrect"); + } + } + } + + va_end(argList); +#endif +} + +NSString *GTMCleanedUserAgentString(NSString *str) { + // Reference http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html + // and http://www-archive.mozilla.org/build/user-agent-strings.html + + if (str == nil) return nil; + + NSMutableString *result = [NSMutableString stringWithString:str]; + + // Replace spaces with underscores + [result replaceOccurrencesOfString:@" " + withString:@"_" + options:0 + range:NSMakeRange(0, [result length])]; + + // Delete http token separators and remaining whitespace + static NSCharacterSet *charsToDelete = nil; + if (charsToDelete == nil) { + // Make a set of unwanted characters + NSString *const kSeparators = @"()<>@,;:\\\"/[]?={}"; + + NSMutableCharacterSet *mutableChars; + mutableChars = [[[NSCharacterSet whitespaceAndNewlineCharacterSet] mutableCopy] autorelease]; + [mutableChars addCharactersInString:kSeparators]; + charsToDelete = [mutableChars copy]; // hang on to an immutable copy + } + + while (1) { + NSRange separatorRange = [result rangeOfCharacterFromSet:charsToDelete]; + if (separatorRange.location == NSNotFound) break; + + [result deleteCharactersInRange:separatorRange]; + }; + + return result; +} + +NSString *GTMSystemVersionString(void) { + NSString *systemString = @""; + +#if TARGET_OS_MAC && !TARGET_OS_IPHONE + // Mac build + static NSString *savedSystemString = nil; + if (savedSystemString == nil) { + // With Gestalt inexplicably deprecated in 10.8, we're reduced to reading + // the system plist file. + NSString *const kPath = @"/System/Library/CoreServices/SystemVersion.plist"; + NSDictionary *plist = [NSDictionary dictionaryWithContentsOfFile:kPath]; + NSString *versString = [plist objectForKey:@"ProductVersion"]; + if ([versString length] == 0) { + versString = @"10.?.?"; + } + savedSystemString = [[NSString alloc] initWithFormat:@"MacOSX/%@", versString]; + } + systemString = savedSystemString; +#elif TARGET_OS_IPHONE + // Compiling against the iPhone SDK + + static NSString *savedSystemString = nil; + if (savedSystemString == nil) { + // Avoid the slowness of calling currentDevice repeatedly on the iPhone + UIDevice* currentDevice = [UIDevice currentDevice]; + + NSString *rawModel = [currentDevice model]; + NSString *model = GTMCleanedUserAgentString(rawModel); + + NSString *systemVersion = [currentDevice systemVersion]; + + savedSystemString = [[NSString alloc] initWithFormat:@"%@/%@", + model, systemVersion]; // "iPod_Touch/2.2" + } + systemString = savedSystemString; + +#elif (GTL_IPHONE || GDATA_IPHONE) + // Compiling iOS libraries against the Mac SDK + systemString = @"iPhone/x.x"; + +#elif defined(_SYS_UTSNAME_H) + // Foundation-only build + struct utsname unameRecord; + uname(&unameRecord); + + systemString = [NSString stringWithFormat:@"%s/%s", + unameRecord.sysname, unameRecord.release]; // "Darwin/8.11.1" +#endif + + return systemString; +} + +// Return a generic name and version for the current application; this avoids +// anonymous server transactions. +NSString *GTMApplicationIdentifier(NSBundle *bundle) { + static NSString *sAppID = nil; + if (sAppID != nil) return sAppID; + + // If there's a bundle ID, use that; otherwise, use the process name + if (bundle == nil) { + bundle = [NSBundle mainBundle]; + } + + NSString *identifier; + NSString *bundleID = [bundle bundleIdentifier]; + if ([bundleID length] > 0) { + identifier = bundleID; + } else { + // Fall back on the procname, prefixed by "proc" to flag that it's + // autogenerated and perhaps unreliable + NSString *procName = [[NSProcessInfo processInfo] processName]; + identifier = [NSString stringWithFormat:@"proc_%@", procName]; + } + + // Clean up whitespace and special characters + identifier = GTMCleanedUserAgentString(identifier); + + // If there's a version number, append that + NSString *version = [bundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; + if ([version length] == 0) { + version = [bundle objectForInfoDictionaryKey:@"CFBundleVersion"]; + } + + // Clean up whitespace and special characters + version = GTMCleanedUserAgentString(version); + + // Glue the two together (cleanup done above or else cleanup would strip the + // slash) + if ([version length] > 0) { + identifier = [identifier stringByAppendingFormat:@"/%@", version]; + } + + sAppID = [identifier copy]; + return sAppID; +} diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTMHTTPFetcherLogging.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTMHTTPFetcherLogging.h new file mode 100644 index 0000000000..d1dacdf3a9 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTMHTTPFetcherLogging.h @@ -0,0 +1,98 @@ +/* Copyright (c) 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "GTMHTTPFetcher.h" + +// GTM HTTP Logging +// +// All traffic using GTMHTTPFetcher can be easily logged. Call +// +// [GTMHTTPFetcher setLoggingEnabled:YES]; +// +// to begin generating log files. +// +// Log files are put into a folder on the desktop called "GTMHTTPDebugLogs" +// unless another directory is specified with +setLoggingDirectory. +// +// In the iPhone simulator, the default logs location is the user's home +// directory in ~/Library/Application Support. On the iPhone device, the +// default logs location is the application's documents directory on the device. +// +// Tip: use the Finder's "Sort By Date" to find the most recent logs. +// +// Each run of an application gets a separate set of log files. An html +// file is generated to simplify browsing the run's http transactions. +// The html file includes javascript links for inline viewing of uploaded +// and downloaded data. +// +// A symlink is created in the logs folder to simplify finding the html file +// for the latest run of the application; the symlink is called +// +// AppName_http_log_newest.html +// +// For better viewing of XML logs, use Camino or Firefox rather than Safari. +// +// Each fetcher may be given a comment to be inserted as a label in the logs, +// such as +// [fetcher setCommentWithFormat:@"retrieve item %@", itemName]; +// +// Projects may define STRIP_GTM_FETCH_LOGGING to remove logging code. + +#if !STRIP_GTM_FETCH_LOGGING + +@interface GTMHTTPFetcher (GTMHTTPFetcherLogging) + +// Note: the default logs directory is ~/Desktop/GTMHTTPDebugLogs; it will be +// created as needed. If a custom directory is set, the directory should +// already exist. ++ (void)setLoggingDirectory:(NSString *)path; ++ (NSString *)loggingDirectory; + +// client apps can turn logging on and off ++ (void)setLoggingEnabled:(BOOL)flag; ++ (BOOL)isLoggingEnabled; + +// client apps can turn off logging to a file if they want to only check +// the fetcher's log property ++ (void)setLoggingToFileEnabled:(BOOL)flag; ++ (BOOL)isLoggingToFileEnabled; + +// client apps can optionally specify process name and date string used in +// log file names ++ (void)setLoggingProcessName:(NSString *)str; ++ (NSString *)loggingProcessName; + ++ (void)setLoggingDateStamp:(NSString *)str; ++ (NSString *)loggingDateStamp; + +// internal; called by fetcher +- (void)logFetchWithError:(NSError *)error; +- (BOOL)logCapturePostStream; + +// Applications may provide alternative body strings to be displayed in the +// log, such as for binary requests or responses. If deferring is turned +// on, the response log will not be sent until deferring is turned off, +// allowing the application to write the response body after the response +// data has been parsed. +- (void)setLogRequestBody:(NSString *)bodyString; +- (NSString *)logRequestBody; +- (void)setLogResponseBody:(NSString *)bodyString; +- (NSString *)logResponseBody; +- (void)setShouldDeferResponseBodyLogging:(BOOL)flag; +- (BOOL)shouldDeferResponseBodyLogging; + +@end + +#endif // !STRIP_GTM_FETCH_LOGGING diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTMHTTPFetcherLogging.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTMHTTPFetcherLogging.m new file mode 100644 index 0000000000..d583c09d46 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTMHTTPFetcherLogging.m @@ -0,0 +1,1101 @@ +/* Copyright (c) 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !STRIP_GTM_FETCH_LOGGING + +#include +#include + +#import "GTMHTTPFetcherLogging.h" + +// Sensitive credential strings are replaced in logs with _snip_ +// +// Apps that must see the contents of sensitive tokens can set this to 1 +#ifndef SKIP_GTM_FETCH_LOGGING_SNIPPING +#define SKIP_GTM_FETCH_LOGGING_SNIPPING 0 +#endif + +// If GTMReadMonitorInputStream is available, it can be used for +// capturing uploaded streams of data +// +// We locally declare methods of GTMReadMonitorInputStream so we +// do not need to import the header, as some projects may not have it available +@interface GTMReadMonitorInputStream : NSInputStream ++ (id)inputStreamWithStream:(NSInputStream *)input; +@property (assign) id readDelegate; +@property (assign) SEL readSelector; +@property (retain) NSArray *runLoopModes; +@end + +// If GTMNSJSONSerialization is available, it is used for formatting JSON +#if (TARGET_OS_MAC && !TARGET_OS_IPHONE && (MAC_OS_X_VERSION_MAX_ALLOWED < 1070)) || \ + (TARGET_OS_IPHONE && (__IPHONE_OS_VERSION_MAX_ALLOWED < 50000)) +@interface GTMNSJSONSerialization : NSObject ++ (NSData *)dataWithJSONObject:(id)obj options:(NSUInteger)opt error:(NSError **)error; ++ (id)JSONObjectWithData:(NSData *)data options:(NSUInteger)opt error:(NSError **)error; +@end +#endif + +// Otherwise, if SBJSON is available, it is used for formatting JSON +@interface GTMFetcherSBJSON +- (void)setHumanReadable:(BOOL)flag; +- (NSString*)stringWithObject:(id)value error:(NSError**)error; +- (id)objectWithString:(NSString*)jsonrep error:(NSError**)error; +@end + +@interface GTMHTTPFetcher (GTMHTTPFetcherLoggingUtilities) ++ (NSString *)headersStringForDictionary:(NSDictionary *)dict; + +- (void)inputStream:(GTMReadMonitorInputStream *)stream + readIntoBuffer:(void *)buffer + length:(NSUInteger)length; + +// internal file utilities for logging ++ (BOOL)fileOrDirExistsAtPath:(NSString *)path; ++ (BOOL)makeDirectoryUpToPath:(NSString *)path; ++ (BOOL)removeItemAtPath:(NSString *)path; ++ (BOOL)createSymbolicLinkAtPath:(NSString *)newPath + withDestinationPath:(NSString *)targetPath; + ++ (NSString *)snipSubstringOfString:(NSString *)originalStr + betweenStartString:(NSString *)startStr + endString:(NSString *)endStr; + ++ (id)JSONObjectWithData:(NSData *)data; ++ (id)stringWithJSONObject:(id)obj; +@end + +@implementation GTMHTTPFetcher (GTMHTTPFetcherLogging) + +// fetchers come and fetchers go, but statics are forever +static BOOL gIsLoggingEnabled = NO; +static BOOL gIsLoggingToFile = YES; +static NSString *gLoggingDirectoryPath = nil; +static NSString *gLoggingDateStamp = nil; +static NSString* gLoggingProcessName = nil; + ++ (void)setLoggingDirectory:(NSString *)path { + [gLoggingDirectoryPath autorelease]; + gLoggingDirectoryPath = [path copy]; +} + ++ (NSString *)loggingDirectory { + + if (!gLoggingDirectoryPath) { + NSArray *arr = nil; +#if GTM_IPHONE && TARGET_IPHONE_SIMULATOR + // default to a directory called GTMHTTPDebugLogs into a sandbox-safe + // directory that a developer can find easily, the application home + arr = [NSArray arrayWithObject:NSHomeDirectory()]; +#elif GTM_IPHONE + // Neither ~/Desktop nor ~/Home is writable on an actual iPhone device. + // Put it in ~/Documents. + arr = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, + NSUserDomainMask, YES); +#else + // default to a directory called GTMHTTPDebugLogs in the desktop folder + arr = NSSearchPathForDirectoriesInDomains(NSDesktopDirectory, + NSUserDomainMask, YES); +#endif + + if ([arr count] > 0) { + NSString *const kGTMLogFolderName = @"GTMHTTPDebugLogs"; + + NSString *desktopPath = [arr objectAtIndex:0]; + NSString *logsFolderPath = [desktopPath stringByAppendingPathComponent:kGTMLogFolderName]; + + BOOL doesFolderExist = [[self class] fileOrDirExistsAtPath:logsFolderPath]; + + if (!doesFolderExist) { + // make the directory + doesFolderExist = [self makeDirectoryUpToPath:logsFolderPath]; + } + + if (doesFolderExist) { + // it's there; store it in the global + gLoggingDirectoryPath = [logsFolderPath copy]; + } + } + } + return gLoggingDirectoryPath; +} + ++ (void)setLoggingEnabled:(BOOL)flag { + gIsLoggingEnabled = flag; +} + ++ (BOOL)isLoggingEnabled { + return gIsLoggingEnabled; +} + ++ (void)setLoggingToFileEnabled:(BOOL)flag { + gIsLoggingToFile = flag; +} + ++ (BOOL)isLoggingToFileEnabled { + return gIsLoggingToFile; +} + ++ (void)setLoggingProcessName:(NSString *)str { + [gLoggingProcessName release]; + gLoggingProcessName = [str copy]; +} + ++ (NSString *)loggingProcessName { + + // get the process name (once per run) replacing spaces with underscores + if (!gLoggingProcessName) { + + NSString *procName = [[NSProcessInfo processInfo] processName]; + NSMutableString *loggingProcessName; + loggingProcessName = [[NSMutableString alloc] initWithString:procName]; + + [loggingProcessName replaceOccurrencesOfString:@" " + withString:@"_" + options:0 + range:NSMakeRange(0, [gLoggingProcessName length])]; + gLoggingProcessName = loggingProcessName; + } + return gLoggingProcessName; +} + ++ (void)setLoggingDateStamp:(NSString *)str { + [gLoggingDateStamp release]; + gLoggingDateStamp = [str copy]; +} + ++ (NSString *)loggingDateStamp { + // we'll pick one date stamp per run, so a run that starts at a later second + // will get a unique results html file + if (!gLoggingDateStamp) { + // produce a string like 08-21_01-41-23PM + + NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease]; + [formatter setFormatterBehavior:NSDateFormatterBehavior10_4]; + [formatter setDateFormat:@"M-dd_hh-mm-ssa"]; + + gLoggingDateStamp = [[formatter stringFromDate:[NSDate date]] retain] ; + } + return gLoggingDateStamp; +} + +// formattedStringFromData returns a prettyprinted string for XML or JSON input, +// and a plain string for other input data +- (NSString *)formattedStringFromData:(NSData *)inputData + contentType:(NSString *)contentType + JSON:(NSDictionary **)outJSON { + if (inputData == nil) return nil; + + // if the content type is JSON and we have the parsing class available, + // use that + if ([contentType hasPrefix:@"application/json"] + && [inputData length] > 5) { + // convert from JSON string to NSObjects and back to a formatted string + NSMutableDictionary *obj = [[self class] JSONObjectWithData:inputData]; + if (obj) { + if (outJSON) *outJSON = obj; + if ([obj isKindOfClass:[NSMutableDictionary class]]) { + // for security and privacy, omit OAuth 2 response access and refresh + // tokens + if ([obj valueForKey:@"refresh_token"] != nil) { + [obj setObject:@"_snip_" forKey:@"refresh_token"]; + } + if ([obj valueForKey:@"access_token"] != nil) { + [obj setObject:@"_snip_" forKey:@"access_token"]; + } + } + NSString *formatted = [[self class] stringWithJSONObject:obj]; + if (formatted) return formatted; + } + } + +#if !GTM_FOUNDATION_ONLY && !GTM_SKIP_LOG_XMLFORMAT + // verify that this data starts with the bytes indicating XML + + NSString *const kXMLLintPath = @"/usr/bin/xmllint"; + static BOOL hasCheckedAvailability = NO; + static BOOL isXMLLintAvailable; + + if (!hasCheckedAvailability) { + isXMLLintAvailable = [[self class] fileOrDirExistsAtPath:kXMLLintPath]; + hasCheckedAvailability = YES; + } + + if (isXMLLintAvailable + && [inputData length] > 5 + && strncmp([inputData bytes], " 0) { + // success + inputData = formattedData; + } + } +#else + // we can't call external tasks on the iPhone; leave the XML unformatted +#endif + + NSString *dataStr = [[[NSString alloc] initWithData:inputData + encoding:NSUTF8StringEncoding] autorelease]; + return dataStr; +} + +- (void)setupStreamLogging { + // if logging is enabled, it needs a buffer to accumulate data from any + // NSInputStream used for uploading. Logging will wrap the input + // stream with a stream that lets us keep a copy the data being read. + if ([GTMHTTPFetcher isLoggingEnabled] + && loggedStreamData_ == nil + && postStream_ != nil) { + loggedStreamData_ = [[NSMutableData alloc] init]; + + BOOL didCapture = [self logCapturePostStream]; + if (!didCapture) { + // upload stream logging requires the class + // GTMReadMonitorInputStream be available + NSString const *str = @"<>"; + [loggedStreamData_ setData:[str dataUsingEncoding:NSUTF8StringEncoding]]; + } + } +} + +- (void)setLogRequestBody:(NSString *)bodyString { + @synchronized(self) { + [logRequestBody_ release]; + logRequestBody_ = [bodyString copy]; + } +} + +- (NSString *)logRequestBody { + @synchronized(self) { + return logRequestBody_; + } +} + +- (void)setLogResponseBody:(NSString *)bodyString { + @synchronized(self) { + [logResponseBody_ release]; + logResponseBody_ = [bodyString copy]; + } +} + +- (NSString *)logResponseBody { + @synchronized(self) { + return logResponseBody_; + } +} + +- (void)setShouldDeferResponseBodyLogging:(BOOL)flag { + @synchronized(self) { + if (flag != shouldDeferResponseBodyLogging_) { + shouldDeferResponseBodyLogging_ = flag; + if (!flag) { + [self performSelectorOnMainThread:@selector(logFetchWithError:) + withObject:nil + waitUntilDone:NO]; + } + } + } +} + +- (BOOL)shouldDeferResponseBodyLogging { + @synchronized(self) { + return shouldDeferResponseBodyLogging_; + } +} + +// stringFromStreamData creates a string given the supplied data +// +// If NSString can create a UTF-8 string from the data, then that is returned. +// +// Otherwise, this routine tries to find a MIME boundary at the beginning of +// the data block, and uses that to break up the data into parts. Each part +// will be used to try to make a UTF-8 string. For parts that fail, a +// replacement string showing the part header and <> is supplied +// in place of the binary data. + +- (NSString *)stringFromStreamData:(NSData *)data + contentType:(NSString *)contentType { + + if (data == nil) return nil; + + // optimistically, see if the whole data block is UTF-8 + NSString *streamDataStr = [self formattedStringFromData:data + contentType:contentType + JSON:NULL]; + if (streamDataStr) return streamDataStr; + + // Munge a buffer by replacing non-ASCII bytes with underscores, + // and turn that munged buffer an NSString. That gives us a string + // we can use with NSScanner. + NSMutableData *mutableData = [NSMutableData dataWithData:data]; + unsigned char *bytes = [mutableData mutableBytes]; + + for (unsigned int idx = 0; idx < [mutableData length]; idx++) { + if (bytes[idx] > 0x7F || bytes[idx] == 0) { + bytes[idx] = '_'; + } + } + + NSString *mungedStr = [[[NSString alloc] initWithData:mutableData + encoding:NSUTF8StringEncoding] autorelease]; + if (mungedStr != nil) { + + // scan for the boundary string + NSString *boundary = nil; + NSScanner *scanner = [NSScanner scannerWithString:mungedStr]; + + if ([scanner scanUpToString:@"\r\n" intoString:&boundary] + && [boundary hasPrefix:@"--"]) { + + // we found a boundary string; use it to divide the string into parts + NSArray *mungedParts = [mungedStr componentsSeparatedByString:boundary]; + + // look at each of the munged parts in the original string, and try to + // convert those into UTF-8 + NSMutableArray *origParts = [NSMutableArray array]; + NSUInteger offset = 0; + for (NSString *mungedPart in mungedParts) { + NSUInteger partSize = [mungedPart length]; + + NSRange range = NSMakeRange(offset, partSize); + NSData *origPartData = [data subdataWithRange:range]; + + NSString *origPartStr = [[[NSString alloc] initWithData:origPartData + encoding:NSUTF8StringEncoding] autorelease]; + if (origPartStr) { + // we could make this original part into UTF-8; use the string + [origParts addObject:origPartStr]; + } else { + // this part can't be made into UTF-8; scan the header, if we can + NSString *header = nil; + NSScanner *headerScanner = [NSScanner scannerWithString:mungedPart]; + if (![headerScanner scanUpToString:@"\r\n\r\n" intoString:&header]) { + // we couldn't find a header + header = @""; + } + + // make a part string with the header and <> + NSString *binStr = [NSString stringWithFormat:@"\r%@\r<<%lu bytes>>\r", + header, (long)(partSize - [header length])]; + [origParts addObject:binStr]; + } + offset += partSize + [boundary length]; + } + + // rejoin the original parts + streamDataStr = [origParts componentsJoinedByString:boundary]; + } + } + + if (!streamDataStr) { + // give up; just make a string showing the uploaded bytes + streamDataStr = [NSString stringWithFormat:@"<<%u bytes>>", + (unsigned int)[data length]]; + } + return streamDataStr; +} + +// logFetchWithError is called following a successful or failed fetch attempt +// +// This method does all the work for appending to and creating log files + +- (void)logFetchWithError:(NSError *)error { + + if (![[self class] isLoggingEnabled]) return; + + // TODO: (grobbins) add Javascript to display response data formatted in hex + + NSString *parentDir = [[self class] loggingDirectory]; + NSString *processName = [[self class] loggingProcessName]; + NSString *dateStamp = [[self class] loggingDateStamp]; + + // make a directory for this run's logs, like + // SyncProto_logs_10-16_01-56-58PM + NSString *dirName = [NSString stringWithFormat:@"%@_log_%@", + processName, dateStamp]; + NSString *logDirectory = [parentDir stringByAppendingPathComponent:dirName]; + + if (gIsLoggingToFile) { + // be sure that the first time this app runs, it's not writing to + // a preexisting folder + static BOOL shouldReuseFolder = NO; + if (!shouldReuseFolder) { + shouldReuseFolder = YES; + NSString *origLogDir = logDirectory; + for (int ctr = 2; ctr < 20; ctr++) { + if (![[self class] fileOrDirExistsAtPath:logDirectory]) break; + + // append a digit + logDirectory = [origLogDir stringByAppendingFormat:@"_%d", ctr]; + } + } + if (![[self class] makeDirectoryUpToPath:logDirectory]) return; + } + // each response's NSData goes into its own xml or txt file, though all + // responses for this run of the app share a main html file. This + // counter tracks all fetch responses for this run of the app. + // + // we'll use a local variable since this routine may be reentered while + // waiting for XML formatting to be completed by an external task + static int zResponseCounter = 0; + int responseCounter = ++zResponseCounter; + + // file name for an image data file + NSString *responseDataFileName = nil; + NSUInteger responseDataLength; + if (downloadFileHandle_) { + responseDataLength = (NSUInteger) [downloadFileHandle_ offsetInFile]; + } else { + responseDataLength = [downloadedData_ length]; + } + + NSURLResponse *response = [self response]; + NSDictionary *responseHeaders = [self responseHeaders]; + + NSString *responseBaseName = nil; + NSString *responseDataStr = nil; + NSDictionary *responseJSON = nil; + + // if there's response data, decide what kind of file to put it in based + // on the first bytes of the file or on the mime type supplied by the server + NSString *responseMIMEType = [response MIMEType]; + BOOL isResponseImage = NO; + NSData *dataToWrite = nil; + + if (responseDataLength > 0) { + NSString *responseDataExtn = nil; + + // generate a response file base name like + responseBaseName = [NSString stringWithFormat:@"fetch_%d_response", + responseCounter]; + + NSString *responseType = [responseHeaders valueForKey:@"Content-Type"]; + responseDataStr = [self formattedStringFromData:downloadedData_ + contentType:responseType + JSON:&responseJSON]; + if (responseDataStr) { + // we were able to make a UTF-8 string from the response data + if ([responseMIMEType isEqual:@"application/atom+xml"] + || [responseMIMEType hasSuffix:@"/xml"]) { + responseDataExtn = @"xml"; + dataToWrite = [responseDataStr dataUsingEncoding:NSUTF8StringEncoding]; + } + } else if ([responseMIMEType isEqual:@"image/jpeg"]) { + responseDataExtn = @"jpg"; + dataToWrite = downloadedData_; + isResponseImage = YES; + } else if ([responseMIMEType isEqual:@"image/gif"]) { + responseDataExtn = @"gif"; + dataToWrite = downloadedData_; + isResponseImage = YES; + } else if ([responseMIMEType isEqual:@"image/png"]) { + responseDataExtn = @"png"; + dataToWrite = downloadedData_; + isResponseImage = YES; + } else { + // add more non-text types here + } + + // if we have an extension, save the raw data in a file with that + // extension + if (responseDataExtn && dataToWrite) { + responseDataFileName = [responseBaseName stringByAppendingPathExtension:responseDataExtn]; + NSString *responseDataFilePath = [logDirectory stringByAppendingPathComponent:responseDataFileName]; + + NSError *downloadedError = nil; + if (gIsLoggingToFile + && ![dataToWrite writeToFile:responseDataFilePath + options:0 + error:&downloadedError]) { + NSLog(@"%@ logging write error:%@ (%@)", + [self class], downloadedError, responseDataFileName); + } + } + } + + // we'll have one main html file per run of the app + NSString *htmlName = @"aperçu_http_log.html"; + NSString *htmlPath =[logDirectory stringByAppendingPathComponent:htmlName]; + + // if the html file exists (from logging previous fetches) we don't need + // to re-write the header or the scripts + BOOL didFileExist = [[self class] fileOrDirExistsAtPath:htmlPath]; + + NSMutableString* outputHTML = [NSMutableString string]; + NSURLRequest *request = [self mutableRequest]; + + // we need a header to say we'll have UTF-8 text + if (!didFileExist) { + [outputHTML appendFormat:@"%@ HTTP fetch log %@", + processName, dateStamp]; + } + + // now write the visible html elements + NSString *copyableFileName = [NSString stringWithFormat:@"fetch_%d.txt", + responseCounter]; + + // write the date & time, the comment, and the link to the plain-text + // (copyable) log + NSString *const dateLineFormat = @"%@      "; + [outputHTML appendFormat:dateLineFormat, [NSDate date]]; + + NSString *comment = [self comment]; + if (comment) { + NSString *const commentFormat = @"%@      "; + [outputHTML appendFormat:commentFormat, comment]; + } + + NSString *const reqRespFormat = @"request/response log
"; + [outputHTML appendFormat:reqRespFormat, copyableFileName]; + + // write the request URL + NSString *requestMethod = [request HTTPMethod]; + NSURL *requestURL = [request URL]; + [outputHTML appendFormat:@"request: %@ %@
\n", + requestMethod, requestURL]; + + // write the request headers + NSDictionary *requestHeaders = [request allHTTPHeaderFields]; + NSUInteger numberOfRequestHeaders = [requestHeaders count]; + if (numberOfRequestHeaders > 0) { + // Indicate if the request is authorized; warn if the request is + // authorized but non-SSL + NSString *auth = [requestHeaders objectForKey:@"Authorization"]; + NSString *headerDetails = @""; + if (auth) { + headerDetails = @"   authorized"; + BOOL isInsecure = [[requestURL scheme] isEqual:@"http"]; + if (isInsecure) { + headerDetails = @"   authorized, non-SSL" + " "; // 26A0 = âš  + } + } + NSString *cookiesHdr = [requestHeaders objectForKey:@"Cookie"]; + if (cookiesHdr) { + headerDetails = [headerDetails stringByAppendingString: + @"   cookies"]; + } + NSString *matchHdr = [requestHeaders objectForKey:@"If-Match"]; + if (matchHdr) { + headerDetails = [headerDetails stringByAppendingString: + @"   if-match"]; + } + matchHdr = [requestHeaders objectForKey:@"If-None-Match"]; + if (matchHdr) { + headerDetails = [headerDetails stringByAppendingString: + @"   if-none-match"]; + } + [outputHTML appendFormat:@"   headers: %d %@
", + (int)numberOfRequestHeaders, headerDetails]; + } else { + [outputHTML appendFormat:@"   headers: none
"]; + } + + // write the request post data, toggleable + NSData *postData; + if (loggedStreamData_) { + postData = loggedStreamData_; + } else if (postData_) { + postData = postData_; + } else { + postData = [request_ HTTPBody]; + } + + NSString *postDataStr = nil; + NSUInteger postDataLength = [postData length]; + NSString *postType = [requestHeaders valueForKey:@"Content-Type"]; + + if (postDataLength > 0) { + [outputHTML appendFormat:@"   data: %d bytes, %@
\n", + (int)postDataLength, postType ? postType : @""]; + + if (logRequestBody_) { + postDataStr = [[logRequestBody_ copy] autorelease]; + [logRequestBody_ release]; + logRequestBody_ = nil; + } else { + postDataStr = [self stringFromStreamData:postData + contentType:postType]; + if (postDataStr) { + // remove OAuth 2 client secret and refresh token + postDataStr = [[self class] snipSubstringOfString:postDataStr + betweenStartString:@"client_secret=" + endString:@"&"]; + + postDataStr = [[self class] snipSubstringOfString:postDataStr + betweenStartString:@"refresh_token=" + endString:@"&"]; + + // remove ClientLogin password + postDataStr = [[self class] snipSubstringOfString:postDataStr + betweenStartString:@"&Passwd=" + endString:@"&"]; + } + } + } else { + // no post data + } + + // write the response status, MIME type, URL + NSInteger status = [self statusCode]; + if (response) { + NSString *statusString = @""; + if (status != 0) { + if (status == 200 || status == 201) { + statusString = [NSString stringWithFormat:@"%ld", (long)status]; + + // report any JSON-RPC error + if ([responseJSON isKindOfClass:[NSDictionary class]]) { + NSDictionary *jsonError = [responseJSON objectForKey:@"error"]; + if ([jsonError isKindOfClass:[NSDictionary class]]) { + NSString *jsonCode = [[jsonError valueForKey:@"code"] description]; + NSString *jsonMessage = [jsonError valueForKey:@"message"]; + if (jsonCode || jsonMessage) { + NSString *const jsonErrFmt = @"   JSON error: %@ %@  ⚑"; // 2691 = âš‘ + statusString = [statusString stringByAppendingFormat:jsonErrFmt, + jsonCode ? jsonCode : @"", + jsonMessage ? jsonMessage : @""]; + } + } + } + } else { + // purple for anything other than 200 or 201 + NSString *flag = (status >= 400 ? @" ⚑" : @""); // 2691 = âš‘ + NSString *const statusFormat = @"%ld %@"; + statusString = [NSString stringWithFormat:statusFormat, + (long)status, flag]; + } + } + + // show the response URL only if it's different from the request URL + NSString *responseURLStr = @""; + NSURL *responseURL = [response URL]; + + if (responseURL && ![responseURL isEqual:[request URL]]) { + NSString *const responseURLFormat = @"response URL:" + " %@
\n"; + responseURLStr = [NSString stringWithFormat:responseURLFormat, + [responseURL absoluteString]]; + } + + [outputHTML appendFormat:@"response:  status %@
\n%@", + statusString, responseURLStr]; + + // Write the response headers + NSUInteger numberOfResponseHeaders = [responseHeaders count]; + if (numberOfResponseHeaders > 0) { + // Indicate if the server is setting cookies + NSString *cookiesSet = [responseHeaders valueForKey:@"Set-Cookie"]; + NSString *cookiesStr = (cookiesSet ? @"  " + "sets cookies" : @""); + // Indicate if the server is redirecting + NSString *location = [responseHeaders valueForKey:@"Location"]; + BOOL isRedirect = (status >= 300 && status <= 399 && location != nil); + NSString *redirectsStr = (isRedirect ? @"  " + "redirects" : @""); + + [outputHTML appendFormat:@"   headers: %d %@ %@
\n", + (int)numberOfResponseHeaders, cookiesStr, redirectsStr]; + } else { + [outputHTML appendString:@"   headers: none
\n"]; + } + } + + // error + if (error) { + [outputHTML appendFormat:@"Error: %@
\n", [error description]]; + } + + // Write the response data + if (responseDataFileName) { + NSString *escapedResponseFile = [responseDataFileName stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + if (isResponseImage) { + // Make a small inline image that links to the full image file + [outputHTML appendFormat:@"   data: %d bytes, %@
", + (int)responseDataLength, responseMIMEType]; + NSString *const fmt = @"image\n"; + [outputHTML appendFormat:fmt, + escapedResponseFile, escapedResponseFile]; + } else { + // The response data was XML; link to the xml file + NSString *const fmt = @"   data: %d bytes, " + "%@   %@\n"; + [outputHTML appendFormat:fmt, + (int)responseDataLength, responseMIMEType, + escapedResponseFile, [escapedResponseFile pathExtension]]; + } + } else { + // The response data was not an image; just show the length and MIME type + [outputHTML appendFormat:@"   data: %d bytes, %@\n", + (int)responseDataLength, responseMIMEType]; + } + + // Make a single string of the request and response, suitable for copying + // to the clipboard and pasting into a bug report + NSMutableString *copyable = [NSMutableString string]; + if (comment) { + [copyable appendFormat:@"%@\n\n", comment]; + } + [copyable appendFormat:@"%@\n", [NSDate date]]; + [copyable appendFormat:@"Request: %@ %@\n", requestMethod, requestURL]; + if ([requestHeaders count] > 0) { + [copyable appendFormat:@"Request headers:\n%@\n", + [[self class] headersStringForDictionary:requestHeaders]]; + } + + if (postDataLength > 0) { + [copyable appendFormat:@"Request body: (%u bytes)\n", + (unsigned int) postDataLength]; + if (postDataStr) { + [copyable appendFormat:@"%@\n", postDataStr]; + } + [copyable appendString:@"\n"]; + } + + if (response) { + [copyable appendFormat:@"Response: status %d\n", (int) status]; + [copyable appendFormat:@"Response headers:\n%@\n", + [[self class] headersStringForDictionary:responseHeaders]]; + [copyable appendFormat:@"Response body: (%u bytes)\n", + (unsigned int) responseDataLength]; + if (responseDataLength > 0) { + if (logResponseBody_) { + responseDataStr = [[logResponseBody_ copy] autorelease]; + [logResponseBody_ release]; + logResponseBody_ = nil; + } + if (responseDataStr != nil) { + [copyable appendFormat:@"%@\n", responseDataStr]; + } else if (status >= 400 && [temporaryDownloadPath_ length] > 0) { + // Try to read in the saved data, which is probably a server error + // message + NSStringEncoding enc; + responseDataStr = [NSString stringWithContentsOfFile:temporaryDownloadPath_ + usedEncoding:&enc + error:NULL]; + if ([responseDataStr length] > 0) { + [copyable appendFormat:@"%@\n", responseDataStr]; + } else { + [copyable appendFormat:@"<<%u bytes to file>>\n", + (unsigned int) responseDataLength]; + } + } else { + // Even though it's redundant, we'll put in text to indicate that all + // the bytes are binary + [copyable appendFormat:@"<<%u bytes>>\n", + (unsigned int) responseDataLength]; + } + } + } + + if (error) { + [copyable appendFormat:@"Error: %@\n", error]; + } + + // Save to log property before adding the separator + self.log = copyable; + + [copyable appendString:@"-----------------------------------------------------------\n"]; + + + // Write the copyable version to another file (linked to at the top of the + // html file, above) + // + // Ideally, something to just copy this to the clipboard like + // Copy here." + // would work everywhere, but it only works in Safari as of 8/2010 + if (gIsLoggingToFile) { + NSString *copyablePath = [logDirectory stringByAppendingPathComponent:copyableFileName]; + NSError *copyableError = nil; + if (![copyable writeToFile:copyablePath + atomically:NO + encoding:NSUTF8StringEncoding + error:©ableError]) { + // Error writing to file + NSLog(@"%@ logging write error:%@ (%@)", + [self class], copyableError, copyablePath); + } + + [outputHTML appendString:@"

"]; + + // Append the HTML to the main output file + const char* htmlBytes = [outputHTML UTF8String]; + NSOutputStream *stream = [NSOutputStream outputStreamToFileAtPath:htmlPath + append:YES]; + [stream open]; + [stream write:(const uint8_t *) htmlBytes maxLength:strlen(htmlBytes)]; + [stream close]; + + // Make a symlink to the latest html + NSString *symlinkName = [NSString stringWithFormat:@"%@_log_newest.html", + processName]; + NSString *symlinkPath = [parentDir stringByAppendingPathComponent:symlinkName]; + + [[self class] removeItemAtPath:symlinkPath]; + [[self class] createSymbolicLinkAtPath:symlinkPath + withDestinationPath:htmlPath]; + +#if GTM_IPHONE + static BOOL gReportedLoggingPath = NO; + if (!gReportedLoggingPath) { + gReportedLoggingPath = YES; + NSLog(@"GTMHTTPFetcher logging to \"%@\"", parentDir); + } +#endif + } +} + +- (BOOL)logCapturePostStream { + // This is called when beginning a fetch. The caller should have already + // verified that logging is enabled, and should have allocated + // loggedStreamData_ as a mutable object. + + // If the class GTMReadMonitorInputStream is not available, bail now, since + // we cannot capture this upload stream + Class monitorClass = NSClassFromString(@"GTMReadMonitorInputStream"); + if (!monitorClass) return NO; + + // If we're logging, we need to wrap the upload stream with our monitor + // stream that will call us back with the bytes being read from the stream + + // Our wrapper will retain the old post stream + [postStream_ autorelease]; + + postStream_ = [monitorClass inputStreamWithStream:postStream_]; + [postStream_ retain]; + + [(GTMReadMonitorInputStream *)postStream_ setReadDelegate:self]; + [(GTMReadMonitorInputStream *)postStream_ setRunLoopModes:[self runLoopModes]]; + + SEL readSel = @selector(inputStream:readIntoBuffer:length:); + [(GTMReadMonitorInputStream *)postStream_ setReadSelector:readSel]; + + return YES; +} + +@end + +@implementation GTMHTTPFetcher (GTMHTTPFetcherLoggingUtilities) + +- (void)inputStream:(GTMReadMonitorInputStream *)stream + readIntoBuffer:(void *)buffer + length:(NSUInteger)length { + // append the captured data + [loggedStreamData_ appendBytes:buffer length:length]; +} + +#pragma mark Internal file routines + +// We implement plain Unix versions of NSFileManager methods to avoid +// NSFileManager's issues with being used from multiple threads + ++ (BOOL)fileOrDirExistsAtPath:(NSString *)path { + struct stat buffer; + int result = stat([path fileSystemRepresentation], &buffer); + return (result == 0); +} + ++ (BOOL)makeDirectoryUpToPath:(NSString *)path { + int result = 0; + + // Recursively create the parent directory of the requested path + NSString *parent = [path stringByDeletingLastPathComponent]; + if (![self fileOrDirExistsAtPath:parent]) { + result = [self makeDirectoryUpToPath:parent]; + } + + // Make the leaf directory + if (result == 0 && ![self fileOrDirExistsAtPath:path]) { + result = mkdir([path fileSystemRepresentation], S_IRWXU); // RWX for owner + } + return (result == 0); +} + ++ (BOOL)removeItemAtPath:(NSString *)path { + int result = unlink([path fileSystemRepresentation]); + return (result == 0); +} + ++ (BOOL)createSymbolicLinkAtPath:(NSString *)newPath + withDestinationPath:(NSString *)targetPath { + int result = symlink([targetPath fileSystemRepresentation], + [newPath fileSystemRepresentation]); + return (result == 0); +} + +#pragma mark Fomatting Utilities + ++ (NSString *)snipSubstringOfString:(NSString *)originalStr + betweenStartString:(NSString *)startStr + endString:(NSString *)endStr { +#if SKIP_GTM_FETCH_LOGGING_SNIPPING + return originalStr; +#else + if (originalStr == nil) return nil; + + // Find the start string, and replace everything between it + // and the end string (or the end of the original string) with "_snip_" + NSRange startRange = [originalStr rangeOfString:startStr]; + if (startRange.location == NSNotFound) return originalStr; + + // We found the start string + NSUInteger originalLength = [originalStr length]; + NSUInteger startOfTarget = NSMaxRange(startRange); + NSRange targetAndRest = NSMakeRange(startOfTarget, + originalLength - startOfTarget); + NSRange endRange = [originalStr rangeOfString:endStr + options:0 + range:targetAndRest]; + NSRange replaceRange; + if (endRange.location == NSNotFound) { + // Found no end marker so replace to end of string + replaceRange = targetAndRest; + } else { + // Replace up to the endStr + replaceRange = NSMakeRange(startOfTarget, + endRange.location - startOfTarget); + } + + NSString *result = [originalStr stringByReplacingCharactersInRange:replaceRange + withString:@"_snip_"]; + return result; +#endif // SKIP_GTM_FETCH_LOGGING_SNIPPING +} + ++ (NSString *)headersStringForDictionary:(NSDictionary *)dict { + // Format the dictionary in http header style, like + // Accept: application/json + // Cache-Control: no-cache + // Content-Type: application/json; charset=utf-8 + // + // Pad the key names, but not beyond 16 chars, since long custom header + // keys just create too much whitespace + NSArray *keys = [[dict allKeys] sortedArrayUsingSelector:@selector(compare:)]; + + NSMutableString *str = [NSMutableString string]; + for (NSString *key in keys) { + NSString *value = [dict valueForKey:key]; + if ([key isEqual:@"Authorization"]) { + // Remove OAuth 1 token + value = [[self class] snipSubstringOfString:value + betweenStartString:@"oauth_token=\"" + endString:@"\""]; + + // Remove OAuth 2 bearer token (draft 16, and older form) + value = [[self class] snipSubstringOfString:value + betweenStartString:@"Bearer " + endString:@"\n"]; + value = [[self class] snipSubstringOfString:value + betweenStartString:@"OAuth " + endString:@"\n"]; + + // Remove Google ClientLogin + value = [[self class] snipSubstringOfString:value + betweenStartString:@"GoogleLogin auth=" + endString:@"\n"]; + } + [str appendFormat:@" %@: %@\n", key, value]; + } + return str; +} + ++ (id)JSONObjectWithData:(NSData *)data { + Class serializer = NSClassFromString(@"NSJSONSerialization"); + if (serializer) { + const NSUInteger kOpts = (1UL << 0); // NSJSONReadingMutableContainers + NSMutableDictionary *obj; + obj = [serializer JSONObjectWithData:data + options:kOpts + error:NULL]; + return obj; + } else { + // Try SBJsonParser or SBJSON + Class jsonParseClass = NSClassFromString(@"SBJsonParser"); + if (!jsonParseClass) { + jsonParseClass = NSClassFromString(@"SBJSON"); + } + if (jsonParseClass) { + GTMFetcherSBJSON *parser = [[[jsonParseClass alloc] init] autorelease]; + NSString *jsonStr = [[[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding] autorelease]; + if (jsonStr) { + NSMutableDictionary *obj = [parser objectWithString:jsonStr error:NULL]; + return obj; + } + } + } + return nil; +} + ++ (id)stringWithJSONObject:(id)obj { + Class serializer = NSClassFromString(@"NSJSONSerialization"); + if (serializer) { + const NSUInteger kOpts = (1UL << 0); // NSJSONWritingPrettyPrinted + NSData *data; + data = [serializer dataWithJSONObject:obj + options:kOpts + error:NULL]; + if (data) { + NSString *jsonStr = [[[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding] autorelease]; + return jsonStr; + } + } else { + // Try SBJsonParser or SBJSON + Class jsonWriterClass = NSClassFromString(@"SBJsonWriter"); + if (!jsonWriterClass) { + jsonWriterClass = NSClassFromString(@"SBJSON"); + } + if (jsonWriterClass) { + GTMFetcherSBJSON *writer = [[[jsonWriterClass alloc] init] autorelease]; + [writer setHumanReadable:YES]; + NSString *jsonStr = [writer stringWithObject:obj error:NULL]; + return jsonStr; + } + } + return nil; +} + +@end + +#endif // !STRIP_GTM_FETCH_LOGGING diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTMHTTPFetcherService.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTMHTTPFetcherService.h new file mode 100644 index 0000000000..9c6cd7548a --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTMHTTPFetcherService.h @@ -0,0 +1,125 @@ +/* Copyright (c) 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTMHTTPFetcherService.h +// + +// The fetcher service class maintains a history to be used by a sequence +// of fetchers objects generated by the service. +// +// Fetchers that do not need to share a history may be generated independently, +// like +// +// GTMHTTPFetcher* myFetcher = [GTMHTTPFetcher fetcherWithRequest:request]; +// +// Fetchers that should share cookies or an ETagged data cache should be +// generated by a common GTMHTTPFetcherService instance, like +// +// GTMHTTPFetcherService *myFetcherService = [[GTMHTTPFetcherService alloc] init]; +// GTMHTTPFetcher* myFirstFetcher = [myFetcherService fetcherWithRequest:request1]; +// GTMHTTPFetcher* mySecondFetcher = [myFetcherService fetcherWithRequest:request2]; + +#import "GTMHTTPFetcher.h" +#import "GTMHTTPFetchHistory.h" + +@interface GTMHTTPFetcherService : NSObject { + @private + NSMutableDictionary *delayedHosts_; + NSMutableDictionary *runningHosts_; + NSUInteger maxRunningFetchersPerHost_; + + GTMHTTPFetchHistory *fetchHistory_; + NSOperationQueue *delegateQueue_; + NSArray *runLoopModes_; + NSString *userAgent_; + NSTimeInterval timeout_; + NSURLCredential *credential_; // username & password + NSURLCredential *proxyCredential_; // credential supplied to proxy servers + NSInteger cookieStorageMethod_; + + BOOL shouldFetchInBackground_; + + id authorizer_; +} + +// Create a fetcher +// +// These methods will return an autoreleased fetcher, but if +// the fetcher is successfully created, the connection will retain the +// fetcher for the life of the connection as well. So the caller doesn't have +// to retain the fetcher explicitly unless they want to be able to monitor +// or cancel it. +- (GTMHTTPFetcher *)fetcherWithRequest:(NSURLRequest *)request; +- (GTMHTTPFetcher *)fetcherWithURL:(NSURL *)requestURL; +- (GTMHTTPFetcher *)fetcherWithURLString:(NSString *)requestURLString; +- (id)fetcherWithRequest:(NSURLRequest *)request + fetcherClass:(Class)fetcherClass; + +// Queues of delayed and running fetchers. Each dictionary contains arrays +// of fetchers, keyed by host +// +// A max value of 0 means no fetchers should be delayed. +// +// The default limit is 10 simultaneous fetchers targeting each host. +@property (assign) NSUInteger maxRunningFetchersPerHost; +@property (retain, readonly) NSDictionary *delayedHosts; +@property (retain, readonly) NSDictionary *runningHosts; + +- (BOOL)isDelayingFetcher:(GTMHTTPFetcher *)fetcher; + +- (NSUInteger)numberOfFetchers; // running + delayed fetchers +- (NSUInteger)numberOfRunningFetchers; +- (NSUInteger)numberOfDelayedFetchers; + +// Search for running or delayed fetchers with the specified URL. +// +// Returns an array of fetcher objects found, or nil if none found. +- (NSArray *)issuedFetchersWithRequestURL:(NSURL *)requestURL; + +- (void)stopAllFetchers; + +// Properties to be applied to each fetcher; +// see GTMHTTPFetcher.h for descriptions +@property (copy) NSString *userAgent; +@property (assign) NSTimeInterval timeout; +@property (retain) NSOperationQueue *delegateQueue; +@property (retain) NSArray *runLoopModes; +@property (retain) NSURLCredential *credential; +@property (retain) NSURLCredential *proxyCredential; +@property (assign) BOOL shouldFetchInBackground; + +// Fetch history +@property (retain) GTMHTTPFetchHistory *fetchHistory; + +@property (assign) NSInteger cookieStorageMethod; +@property (assign) BOOL shouldRememberETags; // default: NO +@property (assign) BOOL shouldCacheETaggedData; // default: NO + +- (void)clearETaggedDataCache; +- (void)clearHistory; + +@property (nonatomic, retain) id authorizer; + +// Spin the run loop, discarding events, until all running and delayed fetchers +// have completed +// +// This is only for use in testing or in tools without a user interface. +// +// Synchronous fetches should never be done by shipping apps; they are +// sufficient reason for rejection from the app store. +- (void)waitForCompletionOfAllFetchersWithTimeout:(NSTimeInterval)timeoutInSeconds; + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTMHTTPFetcherService.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTMHTTPFetcherService.m new file mode 100644 index 0000000000..15909dbbc2 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTMHTTPFetcherService.m @@ -0,0 +1,490 @@ +/* Copyright (c) 2010 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTMHTTPFetcherService.m +// + +#import "GTMHTTPFetcherService.h" + +@interface GTMHTTPFetcher (ServiceMethods) +- (BOOL)beginFetchMayDelay:(BOOL)mayDelay + mayAuthorize:(BOOL)mayAuthorize; +@end + +@interface GTMHTTPFetcherService () +@property (retain, readwrite) NSDictionary *delayedHosts; +@property (retain, readwrite) NSDictionary *runningHosts; + +- (void)detachAuthorizer; +@end + +@implementation GTMHTTPFetcherService + +@synthesize maxRunningFetchersPerHost = maxRunningFetchersPerHost_, + userAgent = userAgent_, + timeout = timeout_, + delegateQueue = delegateQueue_, + runLoopModes = runLoopModes_, + credential = credential_, + proxyCredential = proxyCredential_, + cookieStorageMethod = cookieStorageMethod_, + shouldFetchInBackground = shouldFetchInBackground_, + fetchHistory = fetchHistory_; + +- (id)init { + self = [super init]; + if (self) { + fetchHistory_ = [[GTMHTTPFetchHistory alloc] init]; + delayedHosts_ = [[NSMutableDictionary alloc] init]; + runningHosts_ = [[NSMutableDictionary alloc] init]; + cookieStorageMethod_ = kGTMHTTPFetcherCookieStorageMethodFetchHistory; + + maxRunningFetchersPerHost_ = 10; +} + return self; +} + +- (void)dealloc { + [self detachAuthorizer]; + + [delayedHosts_ release]; + [runningHosts_ release]; + [fetchHistory_ release]; + [userAgent_ release]; + [delegateQueue_ release]; + [runLoopModes_ release]; + [credential_ release]; + [proxyCredential_ release]; + [authorizer_ release]; + + [super dealloc]; +} + +#pragma mark Generate a new fetcher + +- (id)fetcherWithRequest:(NSURLRequest *)request + fetcherClass:(Class)fetcherClass { + GTMHTTPFetcher *fetcher = [fetcherClass fetcherWithRequest:request]; + + fetcher.fetchHistory = self.fetchHistory; + fetcher.delegateQueue = self.delegateQueue; + fetcher.runLoopModes = self.runLoopModes; + fetcher.cookieStorageMethod = self.cookieStorageMethod; + fetcher.credential = self.credential; + fetcher.proxyCredential = self.proxyCredential; + fetcher.shouldFetchInBackground = self.shouldFetchInBackground; + fetcher.authorizer = self.authorizer; + fetcher.service = self; + + NSString *userAgent = self.userAgent; + if ([userAgent length] > 0 + && [request valueForHTTPHeaderField:@"User-Agent"] == nil) { + [fetcher.mutableRequest setValue:userAgent + forHTTPHeaderField:@"User-Agent"]; + } + + NSTimeInterval timeout = self.timeout; + if (timeout > 0.0) { + [fetcher.mutableRequest setTimeoutInterval:timeout]; + } + + return fetcher; +} + +- (GTMHTTPFetcher *)fetcherWithRequest:(NSURLRequest *)request { + return [self fetcherWithRequest:request + fetcherClass:[GTMHTTPFetcher class]]; +} + +- (GTMHTTPFetcher *)fetcherWithURL:(NSURL *)requestURL { + return [self fetcherWithRequest:[NSURLRequest requestWithURL:requestURL]]; +} + +- (GTMHTTPFetcher *)fetcherWithURLString:(NSString *)requestURLString { + return [self fetcherWithURL:[NSURL URLWithString:requestURLString]]; +} + +#pragma mark Queue Management + +- (void)addRunningFetcher:(GTMHTTPFetcher *)fetcher + forHost:(NSString *)host { + // Add to the array of running fetchers for this host, creating the array + // if needed + NSMutableArray *runningForHost = [runningHosts_ objectForKey:host]; + if (runningForHost == nil) { + runningForHost = [NSMutableArray arrayWithObject:fetcher]; + [runningHosts_ setObject:runningForHost forKey:host]; + } else { + [runningForHost addObject:fetcher]; + } +} + +- (void)addDelayedFetcher:(GTMHTTPFetcher *)fetcher + forHost:(NSString *)host { + // Add to the array of delayed fetchers for this host, creating the array + // if needed + NSMutableArray *delayedForHost = [delayedHosts_ objectForKey:host]; + if (delayedForHost == nil) { + delayedForHost = [NSMutableArray arrayWithObject:fetcher]; + [delayedHosts_ setObject:delayedForHost forKey:host]; + } else { + [delayedForHost addObject:fetcher]; + } +} + +- (BOOL)isDelayingFetcher:(GTMHTTPFetcher *)fetcher { + @synchronized(self) { + NSString *host = [[[fetcher mutableRequest] URL] host]; + NSArray *delayedForHost = [delayedHosts_ objectForKey:host]; + NSUInteger idx = [delayedForHost indexOfObjectIdenticalTo:fetcher]; + BOOL isDelayed = (delayedForHost != nil) && (idx != NSNotFound); + return isDelayed; + } +} + +- (BOOL)fetcherShouldBeginFetching:(GTMHTTPFetcher *)fetcher { + // Entry point from the fetcher + @synchronized(self) { + NSString *host = [[[fetcher mutableRequest] URL] host]; + + if ([host length] == 0) { +#if DEBUG + NSAssert1(0, @"%@ lacks host", fetcher); +#endif + return YES; + } + + NSMutableArray *runningForHost = [runningHosts_ objectForKey:host]; + if (runningForHost != nil + && [runningForHost indexOfObjectIdenticalTo:fetcher] != NSNotFound) { +#if DEBUG + NSAssert1(0, @"%@ was already running", fetcher); +#endif + return YES; + } + + // We'll save the host that serves as the key for this fetcher's array + // to avoid any chance of the underlying request changing, stranding + // the fetcher in the wrong array + fetcher.serviceHost = host; + fetcher.thread = [NSThread currentThread]; + + if (maxRunningFetchersPerHost_ == 0 + || maxRunningFetchersPerHost_ > [runningForHost count]) { + [self addRunningFetcher:fetcher forHost:host]; + return YES; + } else { + [self addDelayedFetcher:fetcher forHost:host]; + return NO; + } + } + return YES; +} + +// Fetcher start and stop methods, invoked on the appropriate thread for +// the fetcher +- (void)performSelector:(SEL)sel onStartThreadForFetcher:(GTMHTTPFetcher *)fetcher { + NSOperationQueue *delegateQueue = fetcher.delegateQueue; + NSThread *thread = fetcher.thread; + if (delegateQueue != nil || [thread isEqual:[NSThread currentThread]]) { + // The fetcher should run on the thread we're on now, or there's a delegate + // queue specified so it doesn't matter what thread the fetcher is started + // on, since it will call back on the queue. + [self performSelector:sel withObject:fetcher]; + } else { + // Fetcher must run on a specified thread (and that thread must have a + // run loop.) + [self performSelector:sel + onThread:thread + withObject:fetcher + waitUntilDone:NO]; + } +} + +- (void)startFetcherOnCurrentThread:(GTMHTTPFetcher *)fetcher { + [fetcher beginFetchMayDelay:NO + mayAuthorize:YES]; +} + +- (void)startFetcher:(GTMHTTPFetcher *)fetcher { + [self performSelector:@selector(startFetcherOnCurrentThread:) + onStartThreadForFetcher:fetcher]; +} + +- (void)stopFetcherOnCurrentThread:(GTMHTTPFetcher *)fetcher { + [fetcher stopFetching]; +} + +- (void)stopFetcher:(GTMHTTPFetcher *)fetcher { + [self performSelector:@selector(stopFetcherOnCurrentThread:) + onStartThreadForFetcher:fetcher]; +} + +- (void)fetcherDidStop:(GTMHTTPFetcher *)fetcher { + // Entry point from the fetcher + @synchronized(self) { + NSString *host = fetcher.serviceHost; + if (!host) { + // fetcher has been stopped previously + return; + } + + NSMutableArray *runningForHost = [runningHosts_ objectForKey:host]; + [runningForHost removeObject:fetcher]; + + NSMutableArray *delayedForHost = [delayedHosts_ objectForKey:host]; + [delayedForHost removeObject:fetcher]; + + while ([delayedForHost count] > 0 + && [runningForHost count] < maxRunningFetchersPerHost_) { + // Start another delayed fetcher running, scanning for the minimum + // priority value, defaulting to FIFO for equal priorities + GTMHTTPFetcher *nextFetcher = nil; + for (GTMHTTPFetcher *delayedFetcher in delayedForHost) { + if (nextFetcher == nil + || delayedFetcher.servicePriority < nextFetcher.servicePriority) { + nextFetcher = delayedFetcher; + } + } + + [self addRunningFetcher:nextFetcher forHost:host]; + runningForHost = [runningHosts_ objectForKey:host]; + + [delayedForHost removeObjectIdenticalTo:nextFetcher]; + [self startFetcher:nextFetcher]; + } + + if ([runningForHost count] == 0) { + // None left; remove the empty array + [runningHosts_ removeObjectForKey:host]; + } + + if ([delayedForHost count] == 0) { + [delayedHosts_ removeObjectForKey:host]; + } + + // The fetcher is no longer in the running or the delayed array, + // so remove its host and thread properties + fetcher.serviceHost = nil; + fetcher.thread = nil; + } +} + +- (NSUInteger)numberOfFetchers { + @synchronized(self) { + NSUInteger running = [self numberOfRunningFetchers]; + NSUInteger delayed = [self numberOfDelayedFetchers]; + return running + delayed; + } +} + +- (NSUInteger)numberOfRunningFetchers { + @synchronized(self) { + NSUInteger sum = 0; + for (NSString *host in runningHosts_) { + NSArray *fetchers = [runningHosts_ objectForKey:host]; + sum += [fetchers count]; + } + return sum; + } +} + +- (NSUInteger)numberOfDelayedFetchers { + @synchronized(self) { + NSUInteger sum = 0; + for (NSString *host in delayedHosts_) { + NSArray *fetchers = [delayedHosts_ objectForKey:host]; + sum += [fetchers count]; + } + return sum; + } +} + +- (NSArray *)issuedFetchersWithRequestURL:(NSURL *)requestURL { + @synchronized(self) { + NSMutableArray *array = nil; + NSString *host = [requestURL host]; + if ([host length] == 0) return nil; + + NSURL *absRequestURL = [requestURL absoluteURL]; + + NSArray *runningForHost = [runningHosts_ objectForKey:host]; + for (GTMHTTPFetcher *fetcher in runningForHost) { + NSURL *fetcherURL = [[[fetcher mutableRequest] URL] absoluteURL]; + if ([fetcherURL isEqual:absRequestURL]) { + if (array == nil) { + array = [NSMutableArray array]; + } + [array addObject:fetcher]; + } + } + + NSArray *delayedForHost = [delayedHosts_ objectForKey:host]; + for (GTMHTTPFetcher *fetcher in delayedForHost) { + NSURL *fetcherURL = [[[fetcher mutableRequest] URL] absoluteURL]; + if ([fetcherURL isEqual:absRequestURL]) { + if (array == nil) { + array = [NSMutableArray array]; + } + [array addObject:fetcher]; + } + } + return array; + } +} + +- (void)stopAllFetchers { + @synchronized(self) { + // Remove fetchers from the delayed list to avoid fetcherDidStop: from + // starting more fetchers running as a side effect of stopping one + NSArray *delayedForHosts = [delayedHosts_ allValues]; + [delayedHosts_ removeAllObjects]; + + for (NSArray *delayedForHost in delayedForHosts) { + for (GTMHTTPFetcher *fetcher in delayedForHost) { + [self stopFetcher:fetcher]; + } + } + + NSArray *runningForHosts = [runningHosts_ allValues]; + [runningHosts_ removeAllObjects]; + + for (NSArray *runningForHost in runningForHosts) { + for (GTMHTTPFetcher *fetcher in runningForHost) { + [self stopFetcher:fetcher]; + } + } + } +} + +#pragma mark Fetch History Settings + +// Turn on data caching to receive a copy of previously-retrieved objects. +// Otherwise, fetches may return status 304 (No Change) rather than actual data +- (void)setShouldCacheETaggedData:(BOOL)flag { + self.fetchHistory.shouldCacheETaggedData = flag; +} + +- (BOOL)shouldCacheETaggedData { + return self.fetchHistory.shouldCacheETaggedData; +} + +- (void)setETaggedDataCacheCapacity:(NSUInteger)totalBytes { + self.fetchHistory.memoryCapacity = totalBytes; +} + +- (NSUInteger)ETaggedDataCacheCapacity { + return self.fetchHistory.memoryCapacity; +} + +- (void)setShouldRememberETags:(BOOL)flag { + self.fetchHistory.shouldRememberETags = flag; +} + +- (BOOL)shouldRememberETags { + return self.fetchHistory.shouldRememberETags; +} + +// reset the ETag cache to avoid getting a Not Modified status +// based on prior queries +- (void)clearETaggedDataCache { + [self.fetchHistory clearETaggedDataCache]; +} + +- (void)clearHistory { + [self clearETaggedDataCache]; + [self.fetchHistory removeAllCookies]; +} + +#pragma mark Synchronous Wait for Unit Testing + +- (void)waitForCompletionOfAllFetchersWithTimeout:(NSTimeInterval)timeoutInSeconds { + NSDate* giveUpDate = [NSDate dateWithTimeIntervalSinceNow:timeoutInSeconds]; + BOOL isMainThread = [NSThread isMainThread]; + + while ([self numberOfFetchers] > 0 + && [giveUpDate timeIntervalSinceNow] > 0) { + // Run the current run loop 1/1000 of a second to give the networking + // code a chance to work + if (isMainThread || delegateQueue_ == nil) { + NSDate *stopDate = [NSDate dateWithTimeIntervalSinceNow:0.001]; + [[NSRunLoop currentRunLoop] runUntilDate:stopDate]; + } else { + // Sleep on the delegate queue's background thread. + [NSThread sleepForTimeInterval:0.001]; + } + } +} + +#pragma mark Accessors + +- (NSDictionary *)runningHosts { + return runningHosts_; +} + +- (void)setRunningHosts:(NSDictionary *)dict { + [runningHosts_ autorelease]; + runningHosts_ = [dict mutableCopy]; +} + +- (NSDictionary *)delayedHosts { + return delayedHosts_; +} + +- (void)setDelayedHosts:(NSDictionary *)dict { + [delayedHosts_ autorelease]; + delayedHosts_ = [dict mutableCopy]; +} + +- (id )authorizer { + return authorizer_; +} + +- (void)setAuthorizer:(id )obj { + if (obj != authorizer_) { + [self detachAuthorizer]; + } + + [authorizer_ autorelease]; + authorizer_ = [obj retain]; + + // Use the fetcher service for the authorization fetches if the auth + // object supports fetcher services + if ([authorizer_ respondsToSelector:@selector(setFetcherService:)]) { + [authorizer_ setFetcherService:self]; + } +} + +- (void)detachAuthorizer { + // This method is called by the fetcher service's dealloc and setAuthorizer: + // methods; do not override. + // + // The fetcher service retains the authorizer, and the authorizer has a + // weak pointer to the fetcher service (a non-zeroing pointer for + // compatibility with iOS 4 and Mac OS X 10.5/10.6.) + // + // When this fetcher service no longer uses the authorizer, we want to remove + // the authorizer's dependence on the fetcher service. Authorizers can still + // function without a fetcher service. + if ([authorizer_ respondsToSelector:@selector(fetcherService)]) { + GTMHTTPFetcherService *authFS = [authorizer_ fetcherService]; + if (authFS == self) { + [authorizer_ setFetcherService:nil]; + } + } +} + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTMLogger.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTMLogger.h new file mode 100644 index 0000000000..c4fd140290 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTMLogger.h @@ -0,0 +1,504 @@ +// +// GTMLogger.h +// +// Copyright 2007-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +// Key Abstractions +// ---------------- +// +// This file declares multiple classes and protocols that are used by the +// GTMLogger logging system. The 4 main abstractions used in this file are the +// following: +// +// * logger (GTMLogger) - The main logging class that users interact with. It +// has methods for logging at different levels and uses a log writer, a log +// formatter, and a log filter to get the job done. +// +// * log writer (GTMLogWriter) - Writes a given string to some log file, where +// a "log file" can be a physical file on disk, a POST over HTTP to some URL, +// or even some in-memory structure (e.g., a ring buffer). +// +// * log formatter (GTMLogFormatter) - Given a format string and arguments as +// a va_list, returns a single formatted NSString. A "formatted string" could +// be a string with the date prepended, a string with values in a CSV format, +// or even a string of XML. +// +// * log filter (GTMLogFilter) - Given a formatted log message as an NSString +// and the level at which the message is to be logged, this class will decide +// whether the given message should be logged or not. This is a flexible way +// to filter out messages logged at a certain level, messages that contain +// certain text, or filter nothing out at all. This gives the caller the +// flexibility to dynamically enable debug logging in Release builds. +// +// This file also declares some classes to handle the common log writer, log +// formatter, and log filter cases. Callers can also create their own writers, +// formatters, and filters and they can even build them on top of the ones +// declared here. Keep in mind that your custom writer/formatter/filter may be +// called from multiple threads, so it must be thread-safe. + +#import +#import "GTMDefines.h" + +// Predeclaration of used protocols that are declared later in this file. +@protocol GTMLogWriter, GTMLogFormatter, GTMLogFilter; + +// GTMLogger +// +// GTMLogger is the primary user-facing class for an object-oriented logging +// system. It is built on the concept of log formatters (GTMLogFormatter), log +// writers (GTMLogWriter), and log filters (GTMLogFilter). When a message is +// sent to a GTMLogger to log a message, the message is formatted using the log +// formatter, then the log filter is consulted to see if the message should be +// logged, and if so, the message is sent to the log writer to be written out. +// +// GTMLogger is intended to be a flexible and thread-safe logging solution. Its +// flexibility comes from the fact that GTMLogger instances can be customized +// with user defined formatters, filters, and writers. And these writers, +// filters, and formatters can be combined, stacked, and customized in arbitrary +// ways to suit the needs at hand. For example, multiple writers can be used at +// the same time, and a GTMLogger instance can even be used as another +// GTMLogger's writer. This allows for arbitrarily deep logging trees. +// +// A standard GTMLogger uses a writer that sends messages to standard out, a +// formatter that smacks a timestamp and a few other bits of interesting +// information on the message, and a filter that filters out debug messages from +// release builds. Using the standard log settings, a log message will look like +// the following: +// +// 2007-12-30 10:29:24.177 myapp[4588/0xa07d0f60] [lvl=1] foo= +// +// The output contains the date and time of the log message, the name of the +// process followed by its process ID/thread ID, the log level at which the +// message was logged (in the previous example the level was 1: +// kGTMLoggerLevelDebug), and finally, the user-specified log message itself (in +// this case, the log message was @"foo=%@", foo). +// +// Multiple instances of GTMLogger can be created, each configured their own +// way. Though GTMLogger is not a singleton (in the GoF sense), it does provide +// access to a shared (i.e., globally accessible) GTMLogger instance. This makes +// it convenient for all code in a process to use the same GTMLogger instance. +// The shared GTMLogger instance can also be configured in an arbitrary, and +// these configuration changes will affect all code that logs through the shared +// instance. + +// +// Log Levels +// ---------- +// GTMLogger has 3 different log levels: Debug, Info, and Error. GTMLogger +// doesn't take any special action based on the log level; it simply forwards +// this information on to formatters, filters, and writers, each of which may +// optionally take action based on the level. Since log level filtering is +// performed at runtime, log messages are typically not filtered out at compile +// time. The exception to this rule is that calls to the GTMLoggerDebug() macro +// *ARE* filtered out of non-DEBUG builds. This is to be backwards compatible +// with behavior that many developers are currently used to. Note that this +// means that GTMLoggerDebug(@"hi") will be compiled out of Release builds, but +// [[GTMLogger sharedLogger] logDebug:@"hi"] will NOT be compiled out. +// +// Standard loggers are created with the GTMLogLevelFilter log filter, which +// filters out certain log messages based on log level, and some other settings. +// +// In addition to the -logDebug:, -logInfo:, and -logError: methods defined on +// GTMLogger itself, there are also C macros that make usage of the shared +// GTMLogger instance very convenient. These macros are: +// +// GTMLoggerDebug(...) +// GTMLoggerInfo(...) +// GTMLoggerError(...) +// +// Again, a notable feature of these macros is that GTMLogDebug() calls *will be +// compiled out of non-DEBUG builds*. +// +// Standard Loggers +// ---------------- +// GTMLogger has the concept of "standard loggers". A standard logger is simply +// a logger that is pre-configured with some standard/common writer, formatter, +// and filter combination. Standard loggers are created using the creation +// methods beginning with "standard". The alternative to a standard logger is a +// regular logger, which will send messages to stdout, with no special +// formatting, and no filtering. +// +// How do I use GTMLogger? +// ---------------------- +// The typical way you will want to use GTMLogger is to simply use the +// GTMLogger*() macros for logging from code. That way we can easily make +// changes to the GTMLogger class and simply update the macros accordingly. Only +// your application startup code (perhaps, somewhere in main()) should use the +// GTMLogger class directly in order to configure the shared logger, which all +// of the code using the macros will be using. Again, this is just the typical +// situation. +// +// To be complete, there are cases where you may want to use GTMLogger directly, +// or even create separate GTMLogger instances for some reason. That's fine, +// too. +// +// Examples +// -------- +// The following show some common GTMLogger use cases. +// +// 1. You want to log something as simply as possible. Also, this call will only +// appear in debug builds. In non-DEBUG builds it will be completely removed. +// +// GTMLoggerDebug(@"foo = %@", foo); +// +// 2. The previous example is similar to the following. The major difference is +// that the previous call (example 1) will be compiled out of Release builds +// but this statement will not be compiled out. +// +// [[GTMLogger sharedLogger] logDebug:@"foo = %@", foo]; +// +// 3. Send all logging output from the shared logger to a file. We do this by +// creating an NSFileHandle for writing associated with a file, and setting +// that file handle as the logger's writer. +// +// NSFileHandle *f = [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log" +// create:YES]; +// [[GTMLogger sharedLogger] setWriter:f]; +// GTMLoggerError(@"hi"); // This will be sent to /tmp/f.log +// +// 4. Create a new GTMLogger that will log to a file. This example differs from +// the previous one because here we create a new GTMLogger that is different +// from the shared logger. +// +// GTMLogger *logger = [GTMLogger standardLoggerWithPath:@"/tmp/temp.log"]; +// [logger logInfo:@"hi temp log file"]; +// +// 5. Create a logger that writes to stdout and does NOT do any formatting to +// the log message. This might be useful, for example, when writing a help +// screen for a command-line tool to standard output. +// +// GTMLogger *logger = [GTMLogger logger]; +// [logger logInfo:@"%@ version 0.1 usage", progName]; +// +// 6. Send log output to stdout AND to a log file. The trick here is that +// NSArrays function as composite log writers, which means when an array is +// set as the log writer, it forwards all logging messages to all of its +// contained GTMLogWriters. +// +// // Create array of GTMLogWriters +// NSArray *writers = [NSArray arrayWithObjects: +// [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log" create:YES], +// [NSFileHandle fileHandleWithStandardOutput], nil]; +// +// GTMLogger *logger = [GTMLogger standardLogger]; +// [logger setWriter:writers]; +// [logger logInfo:@"hi"]; // Output goes to stdout and /tmp/f.log +// +// For futher details on log writers, formatters, and filters, see the +// documentation below. +// +// NOTE: GTMLogger is application level logging. By default it does nothing +// with _GTMDevLog/_GTMDevAssert (see GTMDefines.h). An application can choose +// to bridge _GTMDevLog/_GTMDevAssert to GTMLogger by providing macro +// definitions in its prefix header (see GTMDefines.h for how one would do +// that). +// +@interface GTMLogger : NSObject { + @private + id writer_; + id formatter_; + id filter_; +} + +// +// Accessors for the shared logger instance +// + +// Returns a shared/global standard GTMLogger instance. Callers should typically +// use this method to get a GTMLogger instance, unless they explicitly want +// their own instance to configure for their own needs. This is the only method +// that returns a shared instance; all the rest return new GTMLogger instances. ++ (id)sharedLogger; + +// Sets the shared logger instance to |logger|. Future calls to +sharedLogger +// will return |logger| instead. ++ (void)setSharedLogger:(GTMLogger *)logger; + +// +// Creation methods +// + +// Returns a new autoreleased GTMLogger instance that will log to stdout, using +// the GTMLogStandardFormatter, and the GTMLogLevelFilter filter. ++ (id)standardLogger; + +// Same as +standardLogger, but logs to stderr. ++ (id)standardLoggerWithStderr; + +// Same as +standardLogger but levels >= kGTMLoggerLevelError are routed to +// stderr, everything else goes to stdout. ++ (id)standardLoggerWithStdoutAndStderr; + +// Returns a new standard GTMLogger instance with a log writer that will +// write to the file at |path|, and will use the GTMLogStandardFormatter and +// GTMLogLevelFilter classes. If |path| does not exist, it will be created. ++ (id)standardLoggerWithPath:(NSString *)path; + +// Returns an autoreleased GTMLogger instance that will use the specified +// |writer|, |formatter|, and |filter|. ++ (id)loggerWithWriter:(id)writer + formatter:(id)formatter + filter:(id)filter; + +// Returns an autoreleased GTMLogger instance that logs to stdout, with the +// basic formatter, and no filter. The returned logger differs from the logger +// returned by +standardLogger because this one does not do any filtering and +// does not do any special log formatting; this is the difference between a +// "regular" logger and a "standard" logger. ++ (id)logger; + +// Designated initializer. This method returns a GTMLogger initialized with the +// specified |writer|, |formatter|, and |filter|. See the setter methods below +// for what values will be used if nil is passed for a parameter. +- (id)initWithWriter:(id)writer + formatter:(id)formatter + filter:(id)filter; + +// +// Logging methods +// + +// Logs a message at the debug level (kGTMLoggerLevelDebug). +- (void)logDebug:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2); +// Logs a message at the info level (kGTMLoggerLevelInfo). +- (void)logInfo:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2); +// Logs a message at the error level (kGTMLoggerLevelError). +- (void)logError:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2); +// Logs a message at the assert level (kGTMLoggerLevelAssert). +- (void)logAssert:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2); + + +// +// Accessors +// + +// Accessor methods for the log writer. If the log writer is set to nil, +// [NSFileHandle fileHandleWithStandardOutput] is used. +- (id)writer; +- (void)setWriter:(id)writer; + +// Accessor methods for the log formatter. If the log formatter is set to nil, +// GTMLogBasicFormatter is used. This formatter will format log messages in a +// plain printf style. +- (id)formatter; +- (void)setFormatter:(id)formatter; + +// Accessor methods for the log filter. If the log filter is set to nil, +// GTMLogNoFilter is used, which allows all log messages through. +- (id)filter; +- (void)setFilter:(id)filter; + +@end // GTMLogger + + +// Helper functions that are used by the convenience GTMLogger*() macros that +// enable the logging of function names. +@interface GTMLogger (GTMLoggerMacroHelpers) +- (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ... + NS_FORMAT_FUNCTION(2, 3); +- (void)logFuncInfo:(const char *)func msg:(NSString *)fmt, ... + NS_FORMAT_FUNCTION(2, 3); +- (void)logFuncError:(const char *)func msg:(NSString *)fmt, ... + NS_FORMAT_FUNCTION(2, 3); +- (void)logFuncAssert:(const char *)func msg:(NSString *)fmt, ... + NS_FORMAT_FUNCTION(2, 3); +@end // GTMLoggerMacroHelpers + + +// The convenience macros are only defined if they haven't already been defined. +#ifndef GTMLoggerInfo + +// Convenience macros that log to the shared GTMLogger instance. These macros +// are how users should typically log to GTMLogger. Notice that GTMLoggerDebug() +// calls will be compiled out of non-Debug builds. +#define GTMLoggerDebug(...) \ + [[GTMLogger sharedLogger] logFuncDebug:__func__ msg:__VA_ARGS__] +#define GTMLoggerInfo(...) \ + [[GTMLogger sharedLogger] logFuncInfo:__func__ msg:__VA_ARGS__] +#define GTMLoggerError(...) \ + [[GTMLogger sharedLogger] logFuncError:__func__ msg:__VA_ARGS__] +#define GTMLoggerAssert(...) \ + [[GTMLogger sharedLogger] logFuncAssert:__func__ msg:__VA_ARGS__] + +// If we're not in a debug build, remove the GTMLoggerDebug statements. This +// makes calls to GTMLoggerDebug "compile out" of Release builds +#ifndef DEBUG +#undef GTMLoggerDebug +#define GTMLoggerDebug(...) do {} while(0) +#endif + +#endif // !defined(GTMLoggerInfo) + +// Log levels. +typedef enum { + kGTMLoggerLevelUnknown, + kGTMLoggerLevelDebug, + kGTMLoggerLevelInfo, + kGTMLoggerLevelError, + kGTMLoggerLevelAssert, +} GTMLoggerLevel; + + +// +// Log Writers +// + +// Protocol to be implemented by a GTMLogWriter instance. +@protocol GTMLogWriter +// Writes the given log message to where the log writer is configured to write. +- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level; +@end // GTMLogWriter + + +// Simple category on NSFileHandle that makes NSFileHandles valid log writers. +// This is convenient because something like, say, +fileHandleWithStandardError +// now becomes a valid log writer. Log messages are written to the file handle +// with a newline appended. +@interface NSFileHandle (GTMFileHandleLogWriter) +// Opens the file at |path| in append mode, and creates the file with |mode| +// if it didn't previously exist. ++ (id)fileHandleForLoggingAtPath:(NSString *)path mode:(mode_t)mode; +@end // NSFileHandle + + +// This category makes NSArray a GTMLogWriter that can be composed of other +// GTMLogWriters. This is the classic Composite GoF design pattern. When the +// GTMLogWriter -logMessage:level: message is sent to the array, the array +// forwards the message to all of its elements that implement the GTMLogWriter +// protocol. +// +// This is useful in situations where you would like to send log output to +// multiple log writers at the same time. Simply create an NSArray of the log +// writers you wish to use, then set the array as the "writer" for your +// GTMLogger instance. +@interface NSArray (GTMArrayCompositeLogWriter) +@end // GTMArrayCompositeLogWriter + + +// This category adapts the GTMLogger interface so that it can be used as a log +// writer; it's an "adapter" in the GoF Adapter pattern sense. +// +// This is useful when you want to configure a logger to log to a specific +// writer with a specific formatter and/or filter. But you want to also compose +// that with a different log writer that may have its own formatter and/or +// filter. +@interface GTMLogger (GTMLoggerLogWriter) +@end // GTMLoggerLogWriter + + +// +// Log Formatters +// + +// Protocol to be implemented by a GTMLogFormatter instance. +@protocol GTMLogFormatter +// Returns a formatted string using the format specified in |fmt| and the va +// args specified in |args|. +- (NSString *)stringForFunc:(NSString *)func + withFormat:(NSString *)fmt + valist:(va_list)args + level:(GTMLoggerLevel)level NS_FORMAT_FUNCTION(2, 0); +@end // GTMLogFormatter + + +// A basic log formatter that formats a string the same way that NSLog (or +// printf) would. It does not do anything fancy, nor does it add any data of its +// own. +@interface GTMLogBasicFormatter : NSObject + +// Helper method for prettying C99 __func__ and GCC __PRETTY_FUNCTION__ +- (NSString *)prettyNameForFunc:(NSString *)func; + +@end // GTMLogBasicFormatter + + +// A log formatter that formats the log string like the basic formatter, but +// also prepends a timestamp and some basic process info to the message, as +// shown in the following sample output. +// 2007-12-30 10:29:24.177 myapp[4588/0xa07d0f60] [lvl=1] log mesage here +@interface GTMLogStandardFormatter : GTMLogBasicFormatter { + @private + NSDateFormatter *dateFormatter_; // yyyy-MM-dd HH:mm:ss.SSS + NSString *pname_; + pid_t pid_; +} +@end // GTMLogStandardFormatter + + +// +// Log Filters +// + +// Protocol to be imlemented by a GTMLogFilter instance. +@protocol GTMLogFilter +// Returns YES if |msg| at |level| should be filtered out; NO otherwise. +- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level; +@end // GTMLogFilter + + +// A log filter that filters messages at the kGTMLoggerLevelDebug level out of +// non-debug builds. Messages at the kGTMLoggerLevelInfo level are also filtered +// out of non-debug builds unless GTMVerboseLogging is set in the environment or +// the processes's defaults. Messages at the kGTMLoggerLevelError level are +// never filtered. +@interface GTMLogLevelFilter : NSObject +@end // GTMLogLevelFilter + +// A simple log filter that does NOT filter anything out; +// -filterAllowsMessage:level will always return YES. This can be a convenient +// way to enable debug-level logging in release builds (if you so desire). +@interface GTMLogNoFilter : NSObject +@end // GTMLogNoFilter + + +// Base class for custom level filters. Not for direct use, use the minimum +// or maximum level subclasses below. +@interface GTMLogAllowedLevelFilter : NSObject { + @private + NSIndexSet *allowedLevels_; +} +@end + +// A log filter that allows you to set a minimum log level. Messages below this +// level will be filtered. +@interface GTMLogMininumLevelFilter : GTMLogAllowedLevelFilter + +// Designated initializer, logs at levels < |level| will be filtered. +- (id)initWithMinimumLevel:(GTMLoggerLevel)level; + +@end + +// A log filter that allows you to set a maximum log level. Messages whose level +// exceeds this level will be filtered. This is really only useful if you have +// a composite GTMLogger that is sending the other messages elsewhere. +@interface GTMLogMaximumLevelFilter : GTMLogAllowedLevelFilter + +// Designated initializer, logs at levels > |level| will be filtered. +- (id)initWithMaximumLevel:(GTMLoggerLevel)level; + +@end + + +// For subclasses only +@interface GTMLogger (PrivateMethods) + +- (void)logInternalFunc:(const char *)func + format:(NSString *)fmt + valist:(va_list)args + level:(GTMLoggerLevel)level NS_FORMAT_FUNCTION(2, 0); + +@end + diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTMLogger.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTMLogger.m new file mode 100644 index 0000000000..4b40747bfe --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTMLogger.m @@ -0,0 +1,612 @@ +// +// GTMLogger.m +// +// Copyright 2007-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import "GTMLogger.h" +#import "GTMGarbageCollection.h" +#import +#import +#import +#import + + +#if !defined(__clang__) && (__GNUC__*10+__GNUC_MINOR__ >= 42) +// Some versions of GCC (4.2 and below AFAIK) aren't great about supporting +// -Wmissing-format-attribute +// when the function is anything more complex than foo(NSString *fmt, ...). +// You see the error inside the function when you turn ... into va_args and +// attempt to call another function (like vsprintf for example). +// So we just shut off the warning for this file. We reenable it at the end. +#pragma GCC diagnostic ignored "-Wmissing-format-attribute" +#endif // !__clang__ + +// Reference to the shared GTMLogger instance. This is not a singleton, it's +// just an easy reference to one shared instance. +static GTMLogger *gSharedLogger = nil; + + +@implementation GTMLogger + +// Returns a pointer to the shared logger instance. If none exists, a standard +// logger is created and returned. ++ (id)sharedLogger { + @synchronized(self) { + if (gSharedLogger == nil) { + gSharedLogger = [[self standardLogger] retain]; + } + } + return [[gSharedLogger retain] autorelease]; +} + ++ (void)setSharedLogger:(GTMLogger *)logger { + @synchronized(self) { + [gSharedLogger autorelease]; + gSharedLogger = [logger retain]; + } +} + ++ (id)standardLogger { + // Don't trust NSFileHandle not to throw + @try { + id writer = [NSFileHandle fileHandleWithStandardOutput]; + id fr = [[[GTMLogStandardFormatter alloc] init] + autorelease]; + id filter = [[[GTMLogLevelFilter alloc] init] autorelease]; + return [[[self alloc] initWithWriter:writer + formatter:fr + filter:filter] autorelease]; + } + @catch (id e) { + // Ignored + } + return nil; +} + ++ (id)standardLoggerWithStderr { + // Don't trust NSFileHandle not to throw + @try { + id me = [self standardLogger]; + [me setWriter:[NSFileHandle fileHandleWithStandardError]]; + return me; + } + @catch (id e) { + // Ignored + } + return nil; +} + ++ (id)standardLoggerWithStdoutAndStderr { + // We're going to take advantage of the GTMLogger to GTMLogWriter adaptor + // and create a composite logger that an outer "standard" logger can use + // as a writer. Our inner loggers should apply no formatting since the main + // logger does that and we want the caller to be able to change formatters + // or add writers without knowing the inner structure of our composite. + + // Don't trust NSFileHandle not to throw + @try { + GTMLogBasicFormatter *formatter = [[[GTMLogBasicFormatter alloc] init] + autorelease]; + GTMLogger *stdoutLogger = + [self loggerWithWriter:[NSFileHandle fileHandleWithStandardOutput] + formatter:formatter + filter:[[[GTMLogMaximumLevelFilter alloc] + initWithMaximumLevel:kGTMLoggerLevelInfo] + autorelease]]; + GTMLogger *stderrLogger = + [self loggerWithWriter:[NSFileHandle fileHandleWithStandardError] + formatter:formatter + filter:[[[GTMLogMininumLevelFilter alloc] + initWithMinimumLevel:kGTMLoggerLevelError] + autorelease]]; + GTMLogger *compositeWriter = + [self loggerWithWriter:[NSArray arrayWithObjects: + stdoutLogger, stderrLogger, nil] + formatter:formatter + filter:[[[GTMLogNoFilter alloc] init] autorelease]]; + GTMLogger *outerLogger = [self standardLogger]; + [outerLogger setWriter:compositeWriter]; + return outerLogger; + } + @catch (id e) { + // Ignored + } + return nil; +} + ++ (id)standardLoggerWithPath:(NSString *)path { + @try { + NSFileHandle *fh = [NSFileHandle fileHandleForLoggingAtPath:path mode:0644]; + if (fh == nil) return nil; + id me = [self standardLogger]; + [me setWriter:fh]; + return me; + } + @catch (id e) { + // Ignored + } + return nil; +} + ++ (id)loggerWithWriter:(id)writer + formatter:(id)formatter + filter:(id)filter { + return [[[self alloc] initWithWriter:writer + formatter:formatter + filter:filter] autorelease]; +} + ++ (id)logger { + return [[[self alloc] init] autorelease]; +} + +- (id)init { + return [self initWithWriter:nil formatter:nil filter:nil]; +} + +- (id)initWithWriter:(id)writer + formatter:(id)formatter + filter:(id)filter { + if ((self = [super init])) { + [self setWriter:writer]; + [self setFormatter:formatter]; + [self setFilter:filter]; + } + return self; +} + +- (void)dealloc { + // Unlikely, but |writer_| may be an NSFileHandle, which can throw + @try { + [formatter_ release]; + [filter_ release]; + [writer_ release]; + } + @catch (id e) { + // Ignored + } + [super dealloc]; +} + +- (id)writer { + return [[writer_ retain] autorelease]; +} + +- (void)setWriter:(id)writer { + @synchronized(self) { + [writer_ autorelease]; + writer_ = nil; + if (writer == nil) { + // Try to use stdout, but don't trust NSFileHandle + @try { + writer_ = [[NSFileHandle fileHandleWithStandardOutput] retain]; + } + @catch (id e) { + // Leave |writer_| nil + } + } else { + writer_ = [writer retain]; + } + } +} + +- (id)formatter { + return [[formatter_ retain] autorelease]; +} + +- (void)setFormatter:(id)formatter { + @synchronized(self) { + [formatter_ autorelease]; + formatter_ = nil; + if (formatter == nil) { + @try { + formatter_ = [[GTMLogBasicFormatter alloc] init]; + } + @catch (id e) { + // Leave |formatter_| nil + } + } else { + formatter_ = [formatter retain]; + } + } +} + +- (id)filter { + return [[filter_ retain] autorelease]; +} + +- (void)setFilter:(id)filter { + @synchronized(self) { + [filter_ autorelease]; + filter_ = nil; + if (filter == nil) { + @try { + filter_ = [[GTMLogNoFilter alloc] init]; + } + @catch (id e) { + // Leave |filter_| nil + } + } else { + filter_ = [filter retain]; + } + } +} + +- (void)logDebug:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelDebug]; + va_end(args); +} + +- (void)logInfo:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelInfo]; + va_end(args); +} + +- (void)logError:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelError]; + va_end(args); +} + +- (void)logAssert:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelAssert]; + va_end(args); +} + +@end // GTMLogger + +@implementation GTMLogger (GTMLoggerMacroHelpers) + +- (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelDebug]; + va_end(args); +} + +- (void)logFuncInfo:(const char *)func msg:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelInfo]; + va_end(args); +} + +- (void)logFuncError:(const char *)func msg:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelError]; + va_end(args); +} + +- (void)logFuncAssert:(const char *)func msg:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelAssert]; + va_end(args); +} + +@end // GTMLoggerMacroHelpers + +@implementation GTMLogger (PrivateMethods) + +- (void)logInternalFunc:(const char *)func + format:(NSString *)fmt + valist:(va_list)args + level:(GTMLoggerLevel)level { + // Primary point where logging happens, logging should never throw, catch + // everything. + @try { + NSString *fname = func ? [NSString stringWithUTF8String:func] : nil; + NSString *msg = [formatter_ stringForFunc:fname + withFormat:fmt + valist:args + level:level]; + if (msg && [filter_ filterAllowsMessage:msg level:level]) + [writer_ logMessage:msg level:level]; + } + @catch (id e) { + // Ignored + } +} + +@end // PrivateMethods + + +@implementation NSFileHandle (GTMFileHandleLogWriter) + ++ (id)fileHandleForLoggingAtPath:(NSString *)path mode:(mode_t)mode { + int fd = -1; + if (path) { + int flags = O_WRONLY | O_APPEND | O_CREAT; + fd = open([path fileSystemRepresentation], flags, mode); + } + if (fd == -1) return nil; + return [[[self alloc] initWithFileDescriptor:fd + closeOnDealloc:YES] autorelease]; +} + +- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level { + @synchronized(self) { + // Closed pipes should not generate exceptions in our caller. Catch here + // as well [GTMLogger logInternalFunc:...] so that an exception in this + // writer does not prevent other writers from having a chance. + @try { + NSString *line = [NSString stringWithFormat:@"%@\n", msg]; + [self writeData:[line dataUsingEncoding:NSUTF8StringEncoding]]; + } + @catch (id e) { + // Ignored + } + } +} + +@end // GTMFileHandleLogWriter + + +@implementation NSArray (GTMArrayCompositeLogWriter) + +- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level { + @synchronized(self) { + id child = nil; + GTM_FOREACH_OBJECT(child, self) { + if ([child conformsToProtocol:@protocol(GTMLogWriter)]) + [child logMessage:msg level:level]; + } + } +} + +@end // GTMArrayCompositeLogWriter + + +@implementation GTMLogger (GTMLoggerLogWriter) + +- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level { + switch (level) { + case kGTMLoggerLevelDebug: + [self logDebug:@"%@", msg]; + break; + case kGTMLoggerLevelInfo: + [self logInfo:@"%@", msg]; + break; + case kGTMLoggerLevelError: + [self logError:@"%@", msg]; + break; + case kGTMLoggerLevelAssert: + [self logAssert:@"%@", msg]; + break; + default: + // Ignore the message. + break; + } +} + +@end // GTMLoggerLogWriter + + +@implementation GTMLogBasicFormatter + +- (NSString *)prettyNameForFunc:(NSString *)func { + NSString *name = [func stringByTrimmingCharactersInSet: + [NSCharacterSet whitespaceAndNewlineCharacterSet]]; + NSString *function = @"(unknown)"; + if ([name length]) { + if (// Objective C __func__ and __PRETTY_FUNCTION__ + [name hasPrefix:@"-["] || [name hasPrefix:@"+["] || + // C++ __PRETTY_FUNCTION__ and other preadorned formats + [name hasSuffix:@")"]) { + function = name; + } else { + // Assume C99 __func__ + function = [NSString stringWithFormat:@"%@()", name]; + } + } + return function; +} + +- (NSString *)stringForFunc:(NSString *)func + withFormat:(NSString *)fmt + valist:(va_list)args + level:(GTMLoggerLevel)level { + // Performance note: We may want to do a quick check here to see if |fmt| + // contains a '%', and if not, simply return 'fmt'. + if (!(fmt && args)) return nil; + return [[[NSString alloc] initWithFormat:fmt arguments:args] autorelease]; +} + +@end // GTMLogBasicFormatter + + +@implementation GTMLogStandardFormatter + +- (id)init { + if ((self = [super init])) { + dateFormatter_ = [[NSDateFormatter alloc] init]; + [dateFormatter_ setFormatterBehavior:NSDateFormatterBehavior10_4]; + [dateFormatter_ setDateFormat:@"yyyy-MM-dd HH:mm:ss.SSS"]; + pname_ = [[[NSProcessInfo processInfo] processName] copy]; + pid_ = [[NSProcessInfo processInfo] processIdentifier]; + if (!(dateFormatter_ && pname_)) { + [self release]; + return nil; + } + } + return self; +} + +- (void)dealloc { + [dateFormatter_ release]; + [pname_ release]; + [super dealloc]; +} + +- (NSString *)stringForFunc:(NSString *)func + withFormat:(NSString *)fmt + valist:(va_list)args + level:(GTMLoggerLevel)level { + NSString *tstamp = nil; + @synchronized (dateFormatter_) { + tstamp = [dateFormatter_ stringFromDate:[NSDate date]]; + } + return [NSString stringWithFormat:@"%@ %@[%d/%p] [lvl=%d] %@ %@", + tstamp, pname_, pid_, pthread_self(), + level, [self prettyNameForFunc:func], + // |super| has guard for nil |fmt| and |args| + [super stringForFunc:func withFormat:fmt valist:args level:level]]; +} + +@end // GTMLogStandardFormatter + + +@implementation GTMLogLevelFilter + +// Check the environment and the user preferences for the GTMVerboseLogging key +// to see if verbose logging has been enabled. The environment variable will +// override the defaults setting, so check the environment first. +// COV_NF_START +static BOOL IsVerboseLoggingEnabled(void) { + static NSString *const kVerboseLoggingKey = @"GTMVerboseLogging"; + NSString *value = [[[NSProcessInfo processInfo] environment] + objectForKey:kVerboseLoggingKey]; + if (value) { + // Emulate [NSString boolValue] for pre-10.5 + value = [value stringByTrimmingCharactersInSet: + [NSCharacterSet whitespaceAndNewlineCharacterSet]]; + if ([[value uppercaseString] hasPrefix:@"Y"] || + [[value uppercaseString] hasPrefix:@"T"] || + [value intValue]) { + return YES; + } else { + return NO; + } + } + return [[NSUserDefaults standardUserDefaults] boolForKey:kVerboseLoggingKey]; +} +// COV_NF_END + +// In DEBUG builds, log everything. If we're not in a debug build we'll assume +// that we're in a Release build. +- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level { +#if DEBUG + return YES; +#endif + + BOOL allow = YES; + + switch (level) { + case kGTMLoggerLevelDebug: + allow = NO; + break; + case kGTMLoggerLevelInfo: + allow = IsVerboseLoggingEnabled(); + break; + case kGTMLoggerLevelError: + allow = YES; + break; + case kGTMLoggerLevelAssert: + allow = YES; + break; + default: + allow = YES; + break; + } + + return allow; +} + +@end // GTMLogLevelFilter + + +@implementation GTMLogNoFilter + +- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level { + return YES; // Allow everything through +} + +@end // GTMLogNoFilter + + +@implementation GTMLogAllowedLevelFilter + +// Private designated initializer +- (id)initWithAllowedLevels:(NSIndexSet *)levels { + self = [super init]; + if (self != nil) { + allowedLevels_ = [levels retain]; + // Cap min/max level + if (!allowedLevels_ || + // NSIndexSet is unsigned so only check the high bound, but need to + // check both first and last index because NSIndexSet appears to allow + // wraparound. + ([allowedLevels_ firstIndex] > kGTMLoggerLevelAssert) || + ([allowedLevels_ lastIndex] > kGTMLoggerLevelAssert)) { + [self release]; + return nil; + } + } + return self; +} + +- (id)init { + // Allow all levels in default init + return [self initWithAllowedLevels:[NSIndexSet indexSetWithIndexesInRange: + NSMakeRange(kGTMLoggerLevelUnknown, + (kGTMLoggerLevelAssert - kGTMLoggerLevelUnknown + 1))]]; +} + +- (void)dealloc { + [allowedLevels_ release]; + [super dealloc]; +} + +- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level { + return [allowedLevels_ containsIndex:level]; +} + +@end // GTMLogAllowedLevelFilter + + +@implementation GTMLogMininumLevelFilter + +- (id)initWithMinimumLevel:(GTMLoggerLevel)level { + return [super initWithAllowedLevels:[NSIndexSet indexSetWithIndexesInRange: + NSMakeRange(level, + (kGTMLoggerLevelAssert - level + 1))]]; +} + +@end // GTMLogMininumLevelFilter + + +@implementation GTMLogMaximumLevelFilter + +- (id)initWithMaximumLevel:(GTMLoggerLevel)level { + return [super initWithAllowedLevels:[NSIndexSet indexSetWithIndexesInRange: + NSMakeRange(kGTMLoggerLevelUnknown, level + 1)]]; +} + +@end // GTMLogMaximumLevelFilter + +#if !defined(__clang__) && (__GNUC__*10+__GNUC_MINOR__ >= 42) +// See comment at top of file. +#pragma GCC diagnostic error "-Wmissing-format-attribute" +#endif // !__clang__ + diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTMMethodCheck.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTMMethodCheck.h new file mode 100644 index 0000000000..7b0919b0e2 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTMMethodCheck.h @@ -0,0 +1,88 @@ +// +// GTMMethodCheck.h +// +// Copyright 2006-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import +#import +#import + +/// A macro for enforcing debug time checks to make sure all required methods are linked in +// +// When using categories, it can be very easy to forget to include the +// implementation of a category. +// Let's say you had a class foo that depended on method bar of class baz, and +// method bar was implemented as a member of a category. +// You could add the following code: +// @implementation foo +// GTM_METHOD_CHECK(baz, bar) +// @end +// and the code would check to make sure baz was implemented just before main +// was called. This works for both dynamic libraries, and executables. +// +// Classes (or one of their superclasses) being checked must conform to the +// NSObject protocol. We will check this, and spit out a warning if a class does +// not conform to NSObject. +// +// This is not compiled into release builds. + +#ifdef DEBUG + +#ifdef __cplusplus +extern "C" { +#endif + +// If you get an error for GTMMethodCheckMethodChecker not being defined, +// you need to link in GTMMethodCheck.m. We keep it hidden so that we can have +// it living in several separate images without conflict. +// Functions with the ((constructor)) attribute are called after all +loads +// have been called. See "Initializing Objective-C Classes" in +// http://developer.apple.com/documentation/DeveloperTools/Conceptual/DynamicLibraries/Articles/DynamicLibraryDesignGuidelines.html#//apple_ref/doc/uid/TP40002013-DontLinkElementID_20 + +__attribute__ ((constructor, visibility("hidden"))) void GTMMethodCheckMethodChecker(void); + +#ifdef __cplusplus +}; +#endif + +// This is the "magic". +// A) we need a multi layer define here so that the stupid preprocessor +// expands __LINE__ out the way we want it. We need LINE so that each of +// out GTM_METHOD_CHECKs generates a unique class method for the class. +#define GTM_METHOD_CHECK(class, method) GTM_METHOD_CHECK_INNER(class, method, __LINE__) +#define GTM_METHOD_CHECK_INNER(class, method, line) GTM_METHOD_CHECK_INNER_INNER(class, method, line) + +// B) Create up a class method called xxGMethodCheckMethod+class+line that the +// GTMMethodCheckMethodChecker function can look for and call. We +// look for GTMMethodCheckMethodChecker to enforce linkage of +// GTMMethodCheck.m. +#define GTM_METHOD_CHECK_INNER_INNER(class, method, line) \ ++ (void)xxGTMMethodCheckMethod ## class ## line { \ + void (*addr)() = GTMMethodCheckMethodChecker; \ + if (addr && ![class instancesRespondToSelector:@selector(method)] \ + && ![class respondsToSelector:@selector(method)]) { \ + fprintf(stderr, "%s:%d: error: We need method '%s' to be linked in for class '%s'\n", \ + __FILE__, line, #method, #class); \ + exit(EX_SOFTWARE); \ + } \ +} + +#else // !DEBUG + +// Do nothing in release. +#define GTM_METHOD_CHECK(class, method) + +#endif // DEBUG diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTMMethodCheck.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTMMethodCheck.m new file mode 100644 index 0000000000..650d255f5a --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTMMethodCheck.m @@ -0,0 +1,174 @@ +// +// GTMMethodCheck.m +// +// Copyright 2006-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +// Don't want any of this in release builds +#ifdef DEBUG +#import "GTMDefines.h" +#import "GTMMethodCheck.h" +#import "GTMObjC2Runtime.h" +#import + +// Checks to see if the cls passed in (or one of it's superclasses) conforms +// to NSObject protocol. Inheriting from NSObject is the easiest way to do this +// but not all classes (i.e. NSProxy) inherit from NSObject. Also, some classes +// inherit from Object instead of NSObject which is fine, and we'll count as +// conforming to NSObject for our needs. +static BOOL ConformsToNSObjectProtocol(Class cls) { + // If we get nil, obviously doesn't conform. + if (!cls) return NO; + const char *className = class_getName(cls); + if (!className) return NO; + + // We're going to assume that all Apple classes will work + // (and aren't being checked) + // Note to apple: why doesn't obj-c have real namespaces instead of two + // letter hacks? If you name your own classes starting with NS this won't + // work for you. + // Some classes (like _NSZombie) start with _NS. + // On Leopard we have to look for CFObject as well. + // On iPhone we check Object as well + if ((strncmp(className, "NS", 2) == 0) + || (strncmp(className, "_NS", 3) == 0) + || (strncmp(className, "__NS", 4) == 0) + || (strcmp(className, "CFObject") == 0) + || (strcmp(className, "__IncompleteProtocol") == 0) + || (strcmp(className, "__ARCLite__") == 0) + || (strcmp(className, "WebMIMETypeRegistry") == 0) +#if GTM_IPHONE_SDK + || (strcmp(className, "Object") == 0) + || (strcmp(className, "UIKeyboardCandidateUtilities") == 0) +#endif + ) { + return YES; + } + + // iPhone and Mac OS X 10.8 with Obj-C 2 SDKs do not define the |Object| + // class, so we instead test for the |NSObject| class. +#if GTM_IPHONE_SDK || \ + (__OBJC2__ && defined(MAC_OS_X_VERSION_10_8) && \ + MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8) + // Iterate through all the protocols |cls| supports looking for NSObject. + if (cls == [NSObject class] + || class_conformsToProtocol(cls, @protocol(NSObject))) { + return YES; + } +#else + // Iterate through all the protocols |cls| supports looking for NSObject. + if (cls == [Object class] + || class_conformsToProtocol(cls, @protocol(NSObject))) { + return YES; + } +#endif + + // Recursively check the superclasses. + return ConformsToNSObjectProtocol(class_getSuperclass(cls)); +} + +void GTMMethodCheckMethodChecker(void) { + // Run through all the classes looking for class methods that are + // prefixed with xxGMMethodCheckMethod. If it finds one, it calls it. + // See GTMMethodCheck.h to see what it does. +#if !defined(__has_feature) || !__has_feature(objc_arc) + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; +#else + @autoreleasepool { +#endif + int numClasses = 0; + int newNumClasses = objc_getClassList(NULL, 0); + int i; + Class *classes = NULL; + while (numClasses < newNumClasses) { + numClasses = newNumClasses; + classes = (Class *)realloc(classes, sizeof(Class) * numClasses); + _GTMDevAssert(classes, @"Unable to allocate memory for classes"); + newNumClasses = objc_getClassList(classes, numClasses); + } + for (i = 0; i < numClasses && classes; ++i) { + Class cls = classes[i]; + + // Since we are directly calling objc_msgSend, we need to conform to + // @protocol(NSObject), or else we will tumble into a _objc_msgForward + // recursive loop when we try and call a function by name. + if (!ConformsToNSObjectProtocol(cls)) { + // COV_NF_START + _GTMDevLog(@"GTMMethodCheckMethodChecker: Class %s does not conform to " + "@protocol(NSObject), so won't be checked", + class_getName(cls)); + continue; + // COV_NF_END + } + // Since we are looking for a class method (+xxGMMethodCheckMethod...) + // we need to query the isa pointer to see what methods it support, but + // send the method (if it's supported) to the class itself. + unsigned int count; + Class metaClass = objc_getMetaClass(class_getName(cls)); + Method *methods = class_copyMethodList(metaClass, &count); + unsigned int j; + for (j = 0; j < count; ++j) { + SEL selector = method_getName(methods[j]); + const char *name = sel_getName(selector); + if (strstr(name, "xxGTMMethodCheckMethod") == name) { + // Check to make sure that the method we are checking comes + // from the same image that we are in. Since GTMMethodCheckMethodChecker + // is not exported, we should always find the copy in our local + // image. We compare the address of it's image with the address of + // the image which implements the method we want to check. If + // they match we continue. This does two things: + // a) minimizes the amount of calls we make to the xxxGTMMethodCheck + // methods. They should only be called once. + // b) prevents initializers for various classes being called too early + Dl_info methodCheckerInfo; + if (!dladdr(GTMMethodCheckMethodChecker, + &methodCheckerInfo)) { + // COV_NF_START + // Don't know how to force this case in a unittest. + // Certainly hope we never see it. + _GTMDevLog(@"GTMMethodCheckMethodChecker: Unable to get dladdr info " + "for GTMMethodCheckMethodChecker while introspecting +[%s %s]]", + class_getName(cls), name); + continue; + // COV_NF_END + } + Dl_info methodInfo; + if (!dladdr(method_getImplementation(methods[j]), + &methodInfo)) { + // COV_NF_START + // Don't know how to force this case in a unittest + // Certainly hope we never see it. + _GTMDevLog(@"GTMMethodCheckMethodChecker: Unable to get dladdr info " + "for %s while introspecting +[%s %s]]", name, + class_getName(cls), name); + continue; + // COV_NF_END + } + if (methodCheckerInfo.dli_fbase == methodInfo.dli_fbase) { + objc_msgSend(cls, selector); + } + } + } + free(methods); + } + free(classes); +#if !defined(__has_feature) || !__has_feature(objc_arc) + [pool drain]; +#else + } // @autoreleasepool +#endif +} + +#endif // DEBUG diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTMNSDictionary+URLArguments.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTMNSDictionary+URLArguments.h new file mode 100644 index 0000000000..b094411144 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTMNSDictionary+URLArguments.h @@ -0,0 +1,36 @@ +// +// GTMNSDictionary+URLArguments.h +// +// Copyright 2006-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import + +/// Utility for building a URL or POST argument string. +@interface NSDictionary (GTMNSDictionaryURLArgumentsAdditions) + +/// Returns a dictionary of the decoded key-value pairs in a http arguments +/// string of the form key1=value1&key2=value2&...&keyN=valueN. +/// Keys and values will be unescaped automatically. +/// Only the first value for a repeated key is returned. ++ (NSDictionary *)gtm_dictionaryWithHttpArgumentsString:(NSString *)argString; + +/// Gets a string representation of the dictionary in the form +/// key1=value1&key2=value2&...&keyN=valueN, suitable for use as either +/// URL arguments (after a '?') or POST body. Keys and values will be escaped +/// automatically, so should be unescaped in the dictionary. +- (NSString *)gtm_httpArgumentsString; + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTMNSDictionary+URLArguments.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTMNSDictionary+URLArguments.m new file mode 100644 index 0000000000..4799b2ded6 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTMNSDictionary+URLArguments.m @@ -0,0 +1,71 @@ +// +// GTMNSDictionary+URLArguments.m +// +// Copyright 2006-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import "GTMNSDictionary+URLArguments.h" +#import "GTMNSString+URLArguments.h" +#import "GTMMethodCheck.h" +#import "GTMDefines.h" + +@implementation NSDictionary (GTMNSDictionaryURLArgumentsAdditions) + +GTM_METHOD_CHECK(NSString, gtm_stringByEscapingForURLArgument); +GTM_METHOD_CHECK(NSString, gtm_stringByUnescapingFromURLArgument); + ++ (NSDictionary *)gtm_dictionaryWithHttpArgumentsString:(NSString *)argString { + NSMutableDictionary* ret = [NSMutableDictionary dictionary]; + NSArray* components = [argString componentsSeparatedByString:@"&"]; + NSString* component; + // Use reverse order so that the first occurrence of a key replaces + // those subsequent. + GTM_FOREACH_ENUMEREE(component, [components reverseObjectEnumerator]) { + if ([component length] == 0) + continue; + NSRange pos = [component rangeOfString:@"="]; + NSString *key; + NSString *val; + if (pos.location == NSNotFound) { + key = [component gtm_stringByUnescapingFromURLArgument]; + val = @""; + } else { + key = [[component substringToIndex:pos.location] + gtm_stringByUnescapingFromURLArgument]; + val = [[component substringFromIndex:pos.location + pos.length] + gtm_stringByUnescapingFromURLArgument]; + } + // gtm_stringByUnescapingFromURLArgument returns nil on invalid UTF8 + // and NSMutableDictionary raises an exception when passed nil values. + if (!key) key = @""; + if (!val) val = @""; + [ret setObject:val forKey:key]; + } + return ret; +} + +- (NSString *)gtm_httpArgumentsString { + NSMutableArray* arguments = [NSMutableArray arrayWithCapacity:[self count]]; + NSString* key; + GTM_FOREACH_KEY(key, self) { + [arguments addObject:[NSString stringWithFormat:@"%@=%@", + [key gtm_stringByEscapingForURLArgument], + [[[self objectForKey:key] description] gtm_stringByEscapingForURLArgument]]]; + } + + return [arguments componentsJoinedByString:@"&"]; +} + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTMNSString+URLArguments.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTMNSString+URLArguments.h new file mode 100644 index 0000000000..d4c7e09a29 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTMNSString+URLArguments.h @@ -0,0 +1,41 @@ +// +// GTMNSString+URLArguments.h +// +// Copyright 2006-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import + +/// Utilities for encoding and decoding URL arguments. +@interface NSString (GTMNSStringURLArgumentsAdditions) + +/// Returns a string that is escaped properly to be a URL argument. +// +/// This differs from stringByAddingPercentEscapesUsingEncoding: in that it +/// will escape all the reserved characters (per RFC 3986 +/// ) which +/// stringByAddingPercentEscapesUsingEncoding would leave. +/// +/// This will also escape '%', so this should not be used on a string that has +/// already been escaped unless double-escaping is the desired result. +- (NSString*)gtm_stringByEscapingForURLArgument; + +/// Returns the unescaped version of a URL argument +// +/// This has the same behavior as stringByReplacingPercentEscapesUsingEncoding:, +/// except that it will also convert '+' to space. +- (NSString*)gtm_stringByUnescapingFromURLArgument; + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTMNSString+URLArguments.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTMNSString+URLArguments.m new file mode 100644 index 0000000000..46d2c99ed5 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTMNSString+URLArguments.m @@ -0,0 +1,45 @@ +// +// GTMNSString+URLArguments.m +// +// Copyright 2006-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import "GTMNSString+URLArguments.h" +#import "GTMGarbageCollection.h" + +@implementation NSString (GTMNSStringURLArgumentsAdditions) + +- (NSString*)gtm_stringByEscapingForURLArgument { + // Encode all the reserved characters, per RFC 3986 + // () + CFStringRef escaped = + CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, + (CFStringRef)self, + NULL, + (CFStringRef)@"!*'();:@&=+$,/?%#[]", + kCFStringEncodingUTF8); + return GTMCFAutorelease(escaped); +} + +- (NSString*)gtm_stringByUnescapingFromURLArgument { + NSMutableString *resultString = [NSMutableString stringWithString:self]; + [resultString replaceOccurrencesOfString:@"+" + withString:@" " + options:NSLiteralSearch + range:NSMakeRange(0, [resultString length])]; + return [resultString stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; +} + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTMOAuth2Authentication.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTMOAuth2Authentication.h new file mode 100644 index 0000000000..8a7155a347 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTMOAuth2Authentication.h @@ -0,0 +1,333 @@ +/* Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if GTM_INCLUDE_OAUTH2 || !GDATA_REQUIRE_SERVICE_INCLUDES + +// This class implements the OAuth 2 protocol for authorizing requests. +// http://tools.ietf.org/html/draft-ietf-oauth-v2 + +#import + +// GTMHTTPFetcher.h brings in GTLDefines/GDataDefines +#import "GTMHTTPFetcher.h" + +#undef _EXTERN +#undef _INITIALIZE_AS +#ifdef GTMOAUTH2AUTHENTICATION_DEFINE_GLOBALS + #define _EXTERN + #define _INITIALIZE_AS(x) =x +#else + #if defined(__cplusplus) + #define _EXTERN extern "C" + #else + #define _EXTERN extern + #endif + #define _INITIALIZE_AS(x) +#endif + +// Until all OAuth 2 providers are up to the same spec, we'll provide a crude +// way here to override the "Bearer" string in the Authorization header +#ifndef GTM_OAUTH2_BEARER +#define GTM_OAUTH2_BEARER "Bearer" +#endif + +// Service provider name allows stored authorization to be associated with +// the authorizing service +_EXTERN NSString* const kGTMOAuth2ServiceProviderGoogle _INITIALIZE_AS(@"Google"); + +// +// GTMOAuth2SignIn constants, included here for use by clients +// +_EXTERN NSString* const kGTMOAuth2ErrorDomain _INITIALIZE_AS(@"com.google.GTMOAuth2"); + +// Error userInfo keys +_EXTERN NSString* const kGTMOAuth2ErrorMessageKey _INITIALIZE_AS(@"error"); +_EXTERN NSString* const kGTMOAuth2ErrorRequestKey _INITIALIZE_AS(@"request"); +_EXTERN NSString* const kGTMOAuth2ErrorJSONKey _INITIALIZE_AS(@"json"); + +enum { + // Error code indicating that the window was prematurely closed + kGTMOAuth2ErrorWindowClosed = -1000, + kGTMOAuth2ErrorAuthorizationFailed = -1001, + kGTMOAuth2ErrorTokenExpired = -1002, + kGTMOAuth2ErrorTokenUnavailable = -1003, + kGTMOAuth2ErrorUnauthorizableRequest = -1004 +}; + + +// Notifications for token fetches +_EXTERN NSString* const kGTMOAuth2FetchStarted _INITIALIZE_AS(@"kGTMOAuth2FetchStarted"); +_EXTERN NSString* const kGTMOAuth2FetchStopped _INITIALIZE_AS(@"kGTMOAuth2FetchStopped"); + +_EXTERN NSString* const kGTMOAuth2FetcherKey _INITIALIZE_AS(@"fetcher"); +_EXTERN NSString* const kGTMOAuth2FetchTypeKey _INITIALIZE_AS(@"FetchType"); +_EXTERN NSString* const kGTMOAuth2FetchTypeToken _INITIALIZE_AS(@"token"); +_EXTERN NSString* const kGTMOAuth2FetchTypeRefresh _INITIALIZE_AS(@"refresh"); +_EXTERN NSString* const kGTMOAuth2FetchTypeAssertion _INITIALIZE_AS(@"assertion"); +_EXTERN NSString* const kGTMOAuth2FetchTypeUserInfo _INITIALIZE_AS(@"userInfo"); + +// Token-issuance errors +_EXTERN NSString* const kGTMOAuth2ErrorKey _INITIALIZE_AS(@"error"); + +_EXTERN NSString* const kGTMOAuth2ErrorInvalidRequest _INITIALIZE_AS(@"invalid_request"); +_EXTERN NSString* const kGTMOAuth2ErrorInvalidClient _INITIALIZE_AS(@"invalid_client"); +_EXTERN NSString* const kGTMOAuth2ErrorInvalidGrant _INITIALIZE_AS(@"invalid_grant"); +_EXTERN NSString* const kGTMOAuth2ErrorUnauthorizedClient _INITIALIZE_AS(@"unauthorized_client"); +_EXTERN NSString* const kGTMOAuth2ErrorUnsupportedGrantType _INITIALIZE_AS(@"unsupported_grant_type"); +_EXTERN NSString* const kGTMOAuth2ErrorInvalidScope _INITIALIZE_AS(@"invalid_scope"); + +// Notification that sign-in has completed, and token fetches will begin (useful +// for displaying interstitial messages after the window has closed) +_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"); + +// Notification for WebView loading +_EXTERN NSString* const kGTMOAuth2WebViewStartedLoading _INITIALIZE_AS(@"kGTMOAuth2WebViewStartedLoading"); +_EXTERN NSString* const kGTMOAuth2WebViewStoppedLoading _INITIALIZE_AS(@"kGTMOAuth2WebViewStoppedLoading"); +_EXTERN NSString* const kGTMOAuth2WebViewKey _INITIALIZE_AS(@"kGTMOAuth2WebViewKey"); +_EXTERN NSString* const kGTMOAuth2WebViewStopKindKey _INITIALIZE_AS(@"kGTMOAuth2WebViewStopKindKey"); +_EXTERN NSString* const kGTMOAuth2WebViewFinished _INITIALIZE_AS(@"finished"); +_EXTERN NSString* const kGTMOAuth2WebViewFailed _INITIALIZE_AS(@"failed"); +_EXTERN NSString* const kGTMOAuth2WebViewCancelled _INITIALIZE_AS(@"cancelled"); + +// Notification for network loss during html sign-in display +_EXTERN NSString* const kGTMOAuth2NetworkLost _INITIALIZE_AS(@"kGTMOAuthNetworkLost"); +_EXTERN NSString* const kGTMOAuth2NetworkFound _INITIALIZE_AS(@"kGTMOAuthNetworkFound"); + +@interface GTMOAuth2Authentication : NSObject { + @private + NSString *clientID_; + NSString *clientSecret_; + NSString *redirectURI_; + NSMutableDictionary *parameters_; + + // authorization parameters + NSURL *tokenURL_; + NSDate *expirationDate_; + + NSDictionary *additionalTokenRequestParameters_; + + // queue of requests for authorization waiting for a valid access token + GTMHTTPFetcher *refreshFetcher_; + NSMutableArray *authorizationQueue_; + + id fetcherService_; // WEAK + + Class parserClass_; + + BOOL shouldAuthorizeAllRequests_; + + // arbitrary data retained for the user + id userData_; + NSMutableDictionary *properties_; +} + +// OAuth2 standard protocol parameters +// +// These should be the plain strings; any needed escaping will be provided by +// the library. + +// Request properties +@property (copy) NSString *clientID; +@property (copy) NSString *clientSecret; +@property (copy) NSString *redirectURI; +@property (retain) NSString *scope; +@property (retain) NSString *tokenType; +@property (retain) NSString *assertion; +@property (retain) NSString *refreshScope; + +// Apps may optionally add parameters here to be provided to the token +// endpoint on token requests and refreshes +@property (retain) NSDictionary *additionalTokenRequestParameters; + +// Response properties +@property (retain) NSMutableDictionary *parameters; + +@property (retain) NSString *accessToken; +@property (retain) NSString *refreshToken; +@property (retain) NSNumber *expiresIn; +@property (retain) NSString *code; +@property (retain) NSString *errorString; + +// URL for obtaining access tokens +@property (copy) NSURL *tokenURL; + +// Calculated expiration date (expiresIn seconds added to the +// time the access token was received.) +@property (copy) NSDate *expirationDate; + +// Service identifier, like "Google"; not used for authentication +// +// The provider name is just for allowing stored authorization to be associated +// with the authorizing service. +@property (copy) NSString *serviceProvider; + +// User ID; not used for authentication +@property (retain) NSString *userID; + +// User email and verified status; not used for authentication +// +// The verified string can be checked with -boolValue. If the result is false, +// then the email address is listed with the account on the server, but the +// address has not been confirmed as belonging to the owner of the account. +@property (retain) NSString *userEmail; +@property (retain) NSString *userEmailIsVerified; + +// Property indicating if this auth has a refresh or access token so is suitable +// for authorizing a request. This does not guarantee that the token is valid. +@property (readonly) BOOL canAuthorize; + +// Property indicating if this object will authorize plain http request +// (as well as any non-https requests.) Default is NO, only requests with the +// scheme https are authorized, since security may be compromised if tokens +// are sent over the wire using an unencrypted protocol like http. +@property (assign) BOOL shouldAuthorizeAllRequests; + +// userData is retained for the convenience of the caller +@property (retain) id userData; + +// Stored property values are retained for the convenience of the caller +@property (retain) NSDictionary *properties; + +// Property for the optional fetcher service instance to be used to create +// fetchers +// +// Fetcher service objects retain authorizations, so this is weak to avoid +// circular retains. +@property (assign) id fetcherService; // WEAK + +// Alternative JSON parsing class; this should implement the +// GTMOAuth2ParserClass informal protocol. If this property is +// not set, the class SBJSON must be available in the runtime. +@property (assign) Class parserClass; + +// Convenience method for creating an authentication object ++ (id)authenticationWithServiceProvider:(NSString *)serviceProvider + tokenURL:(NSURL *)tokenURL + redirectURI:(NSString *)redirectURI + clientID:(NSString *)clientID + clientSecret:(NSString *)clientSecret; + +// Clear out any authentication values, prepare for a new request fetch +- (void)reset; + +// Main authorization entry points +// +// These will refresh the access token, if necessary, add the access token to +// the request, then invoke the callback. +// +// The request argument may be nil to just force a refresh of the access token, +// if needed. +// +// NOTE: To avoid accidental leaks of bearer tokens, the request must +// be for a URL with the scheme https unless the shouldAuthorizeAllRequests +// property is set. + +// The finish selector should have a signature matching +// - (void)authentication:(GTMOAuth2Authentication *)auth +// request:(NSMutableURLRequest *)request +// finishedWithError:(NSError *)error; + +- (void)authorizeRequest:(NSMutableURLRequest *)request + delegate:(id)delegate + didFinishSelector:(SEL)sel; + +#if NS_BLOCKS_AVAILABLE +- (void)authorizeRequest:(NSMutableURLRequest *)request + completionHandler:(void (^)(NSError *error))handler; +#endif + +// Synchronous entry point; authorizing this way cannot refresh an expired +// access token +- (BOOL)authorizeRequest:(NSMutableURLRequest *)request; + +// If the authentication is waiting for a refresh to complete, spin the run +// loop, discarding events, until the fetch has completed +// +// This is only for use in testing or in tools without a user interface. +- (void)waitForCompletionWithTimeout:(NSTimeInterval)timeoutInSeconds; + + +////////////////////////////////////////////////////////////////////////////// +// +// Internal properties and methods for use by GTMOAuth2SignIn +// + +// Pending fetcher to get a new access token, if any +@property (retain) GTMHTTPFetcher *refreshFetcher; + +// Check if a request is queued up to be authorized +- (BOOL)isAuthorizingRequest:(NSURLRequest *)request; + +// Check if a request appears to be authorized +- (BOOL)isAuthorizedRequest:(NSURLRequest *)request; + +// Stop any pending refresh fetch. This will also cancel the authorization +// for all fetch requests pending authorization. +- (void)stopAuthorization; + +// Prevents authorization callback for a given request. +- (void)stopAuthorizationForRequest:(NSURLRequest *)request; + +// OAuth fetch user-agent header value +- (NSString *)userAgent; + +// Parse and set token and token secret from response data +- (void)setKeysForResponseString:(NSString *)str; +- (void)setKeysForResponseDictionary:(NSDictionary *)dict; + +// Persistent token string for keychain storage +// +// We'll use the format "refresh_token=foo&serviceProvider=bar" so we can +// easily alter what portions of the auth data are stored +// +// Use these methods for serialization +- (NSString *)persistenceResponseString; +- (void)setKeysForPersistenceResponseString:(NSString *)str; + +// method to begin fetching an access token, used by the sign-in object +- (GTMHTTPFetcher *)beginTokenFetchWithDelegate:(id)delegate + didFinishSelector:(SEL)finishedSel; + +// Entry point to post a notification about a fetcher currently used for +// obtaining or refreshing a token; the sign-in object will also use this +// to indicate when the user's email address is being fetched. +// +// Fetch type constants are above under "notifications for token fetches" +- (void)notifyFetchIsRunning:(BOOL)isStarting + fetcher:(GTMHTTPFetcher *)fetcher + type:(NSString *)fetchType; + +// Arbitrary key-value properties retained for the user +- (void)setProperty:(id)obj forKey:(NSString *)key; +- (id)propertyForKey:(NSString *)key; + +// +// Utilities +// + ++ (NSString *)encodedOAuthValueForString:(NSString *)str; + ++ (NSString *)encodedQueryParametersForDictionary:(NSDictionary *)dict; + ++ (NSDictionary *)dictionaryWithResponseString:(NSString *)responseStr; + ++ (NSString *)scopeWithStrings:(NSString *)firsStr, ... NS_REQUIRES_NIL_TERMINATION; +@end + +#endif // GTM_INCLUDE_OAUTH2 || !GDATA_REQUIRE_SERVICE_INCLUDES diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTMOAuth2Authentication.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTMOAuth2Authentication.m new file mode 100644 index 0000000000..0ea5fd7e0d --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTMOAuth2Authentication.m @@ -0,0 +1,1231 @@ +/* Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if GTM_INCLUDE_OAUTH2 || !GDATA_REQUIRE_SERVICE_INCLUDES + +#define GTMOAUTH2AUTHENTICATION_DEFINE_GLOBALS 1 +#import "GTMOAuth2Authentication.h" + +// standard OAuth keys +static NSString *const kOAuth2AccessTokenKey = @"access_token"; +static NSString *const kOAuth2RefreshTokenKey = @"refresh_token"; +static NSString *const kOAuth2ClientIDKey = @"client_id"; +static NSString *const kOAuth2ClientSecretKey = @"client_secret"; +static NSString *const kOAuth2RedirectURIKey = @"redirect_uri"; +static NSString *const kOAuth2ResponseTypeKey = @"response_type"; +static NSString *const kOAuth2ScopeKey = @"scope"; +static NSString *const kOAuth2ErrorKey = @"error"; +static NSString *const kOAuth2TokenTypeKey = @"token_type"; +static NSString *const kOAuth2ExpiresInKey = @"expires_in"; +static NSString *const kOAuth2CodeKey = @"code"; +static NSString *const kOAuth2AssertionKey = @"assertion"; +static NSString *const kOAuth2RefreshScopeKey = @"refreshScope"; + +// additional persistent keys +static NSString *const kServiceProviderKey = @"serviceProvider"; +static NSString *const kUserIDKey = @"userID"; +static NSString *const kUserEmailKey = @"email"; +static NSString *const kUserEmailIsVerifiedKey = @"isVerified"; + +// fetcher keys +static NSString *const kTokenFetchDelegateKey = @"delegate"; +static NSString *const kTokenFetchSelectorKey = @"sel"; + +static NSString *const kRefreshFetchArgsKey = @"requestArgs"; + +// If GTMNSJSONSerialization is available, it is used for formatting JSON +#if (TARGET_OS_MAC && !TARGET_OS_IPHONE && (MAC_OS_X_VERSION_MAX_ALLOWED < 1070)) || \ + (TARGET_OS_IPHONE && (__IPHONE_OS_VERSION_MAX_ALLOWED < 50000)) +@interface GTMNSJSONSerialization : NSObject ++ (id)JSONObjectWithData:(NSData *)data options:(NSUInteger)opt error:(NSError **)error; +@end +#endif + +@interface GTMOAuth2ParserClass : NSObject +// just enough of SBJSON to be able to parse +- (id)objectWithString:(NSString*)repr error:(NSError**)error; +@end + +// wrapper class for requests needing authorization and their callbacks +@interface GTMOAuth2AuthorizationArgs : NSObject { + @private + NSMutableURLRequest *request_; + id delegate_; + SEL sel_; + id completionHandler_; + NSThread *thread_; + NSError *error_; +} + +@property (retain) NSMutableURLRequest *request; +@property (retain) id delegate; +@property (assign) SEL selector; +@property (copy) id completionHandler; +@property (retain) NSThread *thread; +@property (retain) NSError *error; + ++ (GTMOAuth2AuthorizationArgs *)argsWithRequest:(NSMutableURLRequest *)req + delegate:(id)delegate + selector:(SEL)sel + completionHandler:(id)completionHandler + thread:(NSThread *)thread; +@end + +@implementation GTMOAuth2AuthorizationArgs + +@synthesize request = request_, + delegate = delegate_, + selector = sel_, + completionHandler = completionHandler_, + thread = thread_, + error = error_; + ++ (GTMOAuth2AuthorizationArgs *)argsWithRequest:(NSMutableURLRequest *)req + delegate:(id)delegate + selector:(SEL)sel + completionHandler:(id)completionHandler + thread:(NSThread *)thread { + GTMOAuth2AuthorizationArgs *obj; + obj = [[[GTMOAuth2AuthorizationArgs alloc] init] autorelease]; + obj.request = req; + obj.delegate = delegate; + obj.selector = sel; + obj.completionHandler = completionHandler; + obj.thread = thread; + return obj; +} + +- (void)dealloc { + [request_ release]; + [delegate_ release]; + [completionHandler_ release]; + [thread_ release]; + [error_ release]; + + [super dealloc]; +} +@end + + +@interface GTMOAuth2Authentication () + +@property (retain) NSMutableArray *authorizationQueue; + +- (void)setKeysForResponseJSONData:(NSData *)data; + +- (BOOL)authorizeRequestArgs:(GTMOAuth2AuthorizationArgs *)args; + +- (BOOL)authorizeRequestImmediateArgs:(GTMOAuth2AuthorizationArgs *)args; + +- (BOOL)shouldRefreshAccessToken; + +- (void)updateExpirationDate; + +- (NSDictionary *)dictionaryWithJSONData:(NSData *)data; + +- (void)tokenFetcher:(GTMHTTPFetcher *)fetcher + finishedWithData:(NSData *)data + error:(NSError *)error; + +- (void)auth:(GTMOAuth2Authentication *)auth +finishedRefreshWithFetcher:(GTMHTTPFetcher *)fetcher + error:(NSError *)error; + +- (void)invokeCallbackArgs:(GTMOAuth2AuthorizationArgs *)args; + ++ (void)invokeDelegate:(id)delegate + selector:(SEL)sel + object:(id)obj1 + object:(id)obj2 + object:(id)obj3; + ++ (NSString *)unencodedOAuthParameterForString:(NSString *)str; ++ (NSString *)encodedQueryParametersForDictionary:(NSDictionary *)dict; + ++ (NSDictionary *)dictionaryWithResponseData:(NSData *)data; + +@end + +@implementation GTMOAuth2Authentication + +@synthesize clientID = clientID_, + clientSecret = clientSecret_, + redirectURI = redirectURI_, + parameters = parameters_, + tokenURL = tokenURL_, + expirationDate = expirationDate_, + additionalTokenRequestParameters = additionalTokenRequestParameters_, + refreshFetcher = refreshFetcher_, + fetcherService = fetcherService_, + parserClass = parserClass_, + shouldAuthorizeAllRequests = shouldAuthorizeAllRequests_, + userData = userData_, + properties = properties_, + authorizationQueue = authorizationQueue_; + +// Response parameters +@dynamic accessToken, + refreshToken, + code, + assertion, + refreshScope, + errorString, + tokenType, + scope, + expiresIn, + serviceProvider, + userEmail, + userEmailIsVerified; + +@dynamic canAuthorize; + ++ (id)authenticationWithServiceProvider:(NSString *)serviceProvider + tokenURL:(NSURL *)tokenURL + redirectURI:(NSString *)redirectURI + clientID:(NSString *)clientID + clientSecret:(NSString *)clientSecret { + GTMOAuth2Authentication *obj = [[[self alloc] init] autorelease]; + obj.serviceProvider = serviceProvider; + obj.tokenURL = tokenURL; + obj.redirectURI = redirectURI; + obj.clientID = clientID; + obj.clientSecret = clientSecret; + return obj; +} + +- (id)init { + self = [super init]; + if (self) { + authorizationQueue_ = [[NSMutableArray alloc] init]; + parameters_ = [[NSMutableDictionary alloc] init]; + } + return self; +} + +- (NSString *)description { + NSArray *props = [NSArray arrayWithObjects:@"accessToken", @"refreshToken", + @"code", @"assertion", @"expirationDate", @"errorString", + nil]; + NSMutableString *valuesStr = [NSMutableString string]; + NSString *separator = @""; + for (NSString *prop in props) { + id result = [self valueForKey:prop]; + if (result) { + [valuesStr appendFormat:@"%@%@=\"%@\"", separator, prop, result]; + separator = @", "; + } + } + + return [NSString stringWithFormat:@"%@ %p: {%@}", + [self class], self, valuesStr]; +} + +- (void)dealloc { + [clientID_ release]; + [clientSecret_ release]; + [redirectURI_ release]; + [parameters_ release]; + [tokenURL_ release]; + [expirationDate_ release]; + [additionalTokenRequestParameters_ release]; + [refreshFetcher_ release]; + [authorizationQueue_ release]; + [userData_ release]; + [properties_ release]; + + [super dealloc]; +} + +#pragma mark - + +- (void)setKeysForResponseDictionary:(NSDictionary *)dict { + if (dict == nil) return; + + // If a new code or access token is being set, remove the old expiration + NSString *newCode = [dict objectForKey:kOAuth2CodeKey]; + NSString *newAccessToken = [dict objectForKey:kOAuth2AccessTokenKey]; + if (newCode || newAccessToken) { + self.expiresIn = nil; + } + + BOOL didRefreshTokenChange = NO; + NSString *refreshToken = [dict objectForKey:kOAuth2RefreshTokenKey]; + if (refreshToken) { + NSString *priorRefreshToken = self.refreshToken; + + if (priorRefreshToken != refreshToken + && (priorRefreshToken == nil + || ![priorRefreshToken isEqual:refreshToken])) { + didRefreshTokenChange = YES; + } + } + + [self.parameters addEntriesFromDictionary:dict]; + [self updateExpirationDate]; + + if (didRefreshTokenChange) { + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc postNotificationName:kGTMOAuth2RefreshTokenChanged + object:self + userInfo:nil]; + } + // NSLog(@"keys set ----------------------------\n%@", dict); +} + +- (void)setKeysForResponseString:(NSString *)str { + NSDictionary *dict = [[self class] dictionaryWithResponseString:str]; + [self setKeysForResponseDictionary:dict]; +} + +- (void)setKeysForResponseJSONData:(NSData *)data { + NSDictionary *dict = [self dictionaryWithJSONData:data]; + [self setKeysForResponseDictionary:dict]; +} + +- (NSDictionary *)dictionaryWithJSONData:(NSData *)data { + NSMutableDictionary *obj = nil; + NSError *error = nil; + + Class serializer = NSClassFromString(@"NSJSONSerialization"); + if (serializer) { + const NSUInteger kOpts = (1UL << 0); // NSJSONReadingMutableContainers + obj = [serializer JSONObjectWithData:data + options:kOpts + error:&error]; +#if DEBUG + if (error) { + NSString *str = [[[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding] autorelease]; + NSLog(@"NSJSONSerialization error %@ parsing %@", + error, str); + } +#endif + return obj; + } else { + // try SBJsonParser or SBJSON + Class jsonParseClass = NSClassFromString(@"SBJsonParser"); + if (!jsonParseClass) { + jsonParseClass = NSClassFromString(@"SBJSON"); + } + if (jsonParseClass) { + GTMOAuth2ParserClass *parser = [[[jsonParseClass alloc] init] autorelease]; + NSString *jsonStr = [[[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding] autorelease]; + if (jsonStr) { + obj = [parser objectWithString:jsonStr error:&error]; +#if DEBUG + if (error) { + NSLog(@"%@ error %@ parsing %@", NSStringFromClass(jsonParseClass), + error, jsonStr); + } +#endif + return obj; + } + } else { +#if DEBUG + NSAssert(0, @"GTMOAuth2Authentication: No parser available"); +#endif + } + } + return nil; +} + +#pragma mark Authorizing Requests + +// General entry point for authorizing requests + +#if NS_BLOCKS_AVAILABLE +// Authorizing with a completion block +- (void)authorizeRequest:(NSMutableURLRequest *)request + completionHandler:(void (^)(NSError *error))handler { + + GTMOAuth2AuthorizationArgs *args; + args = [GTMOAuth2AuthorizationArgs argsWithRequest:request + delegate:nil + selector:NULL + completionHandler:handler + thread:[NSThread currentThread]]; + [self authorizeRequestArgs:args]; +} +#endif + +// Authorizing with a callback selector +// +// Selector has the signature +// - (void)authentication:(GTMOAuth2Authentication *)auth +// request:(NSMutableURLRequest *)request +// finishedWithError:(NSError *)error; +- (void)authorizeRequest:(NSMutableURLRequest *)request + delegate:(id)delegate + didFinishSelector:(SEL)sel { + GTMAssertSelectorNilOrImplementedWithArgs(delegate, sel, + @encode(GTMOAuth2Authentication *), + @encode(NSMutableURLRequest *), + @encode(NSError *), 0); + + GTMOAuth2AuthorizationArgs *args; + args = [GTMOAuth2AuthorizationArgs argsWithRequest:request + delegate:delegate + selector:sel + completionHandler:nil + thread:[NSThread currentThread]]; + [self authorizeRequestArgs:args]; +} + +// Internal routine common to delegate and block invocations +- (BOOL)authorizeRequestArgs:(GTMOAuth2AuthorizationArgs *)args { + BOOL didAttempt = NO; + + @synchronized(authorizationQueue_) { + + BOOL shouldRefresh = [self shouldRefreshAccessToken]; + + if (shouldRefresh) { + // attempt to refresh now; once we have a fresh access token, we will + // authorize the request and call back to the user + didAttempt = YES; + + if (self.refreshFetcher == nil) { + // there's not already a refresh pending + SEL finishedSel = @selector(auth:finishedRefreshWithFetcher:error:); + self.refreshFetcher = [self beginTokenFetchWithDelegate:self + didFinishSelector:finishedSel]; + if (self.refreshFetcher) { + [authorizationQueue_ addObject:args]; + } + } else { + // there's already a refresh pending + [authorizationQueue_ addObject:args]; + } + } + + if (!shouldRefresh || self.refreshFetcher == nil) { + // we're not fetching a new access token, so we can authorize the request + // now + didAttempt = [self authorizeRequestImmediateArgs:args]; + } + } + return didAttempt; +} + +- (void)auth:(GTMOAuth2Authentication *)auth +finishedRefreshWithFetcher:(GTMHTTPFetcher *)fetcher + error:(NSError *)error { + @synchronized(authorizationQueue_) { + // If there's an error, we want to try using the old access token anyway, + // in case it's a backend problem preventing refresh, in which case + // access tokens past their expiration date may still work + + self.refreshFetcher = nil; + + // Swap in a new auth queue in case the callbacks try to immediately auth + // another request + NSArray *pendingAuthQueue = [NSArray arrayWithArray:authorizationQueue_]; + [authorizationQueue_ removeAllObjects]; + + BOOL hasAccessToken = ([self.accessToken length] > 0); + + if (hasAccessToken && error == nil) { + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc postNotificationName:kGTMOAuth2AccessTokenRefreshed + object:self + userInfo:nil]; + } + + for (GTMOAuth2AuthorizationArgs *args in pendingAuthQueue) { + if (!hasAccessToken && args.error == nil) { + args.error = error; + } + + [self authorizeRequestImmediateArgs:args]; + } + } +} + +- (BOOL)isAuthorizingRequest:(NSURLRequest *)request { + BOOL wasFound = NO; + @synchronized(authorizationQueue_) { + for (GTMOAuth2AuthorizationArgs *args in authorizationQueue_) { + if ([args request] == request) { + wasFound = YES; + break; + } + } + } + return wasFound; +} + +- (BOOL)isAuthorizedRequest:(NSURLRequest *)request { + NSString *authStr = [request valueForHTTPHeaderField:@"Authorization"]; + return ([authStr length] > 0); +} + +- (void)stopAuthorization { + @synchronized(authorizationQueue_) { + [authorizationQueue_ removeAllObjects]; + + [self.refreshFetcher stopFetching]; + self.refreshFetcher = nil; + } +} + +- (void)stopAuthorizationForRequest:(NSURLRequest *)request { + @synchronized(authorizationQueue_) { + NSUInteger argIndex = 0; + BOOL found = NO; + for (GTMOAuth2AuthorizationArgs *args in authorizationQueue_) { + if ([args request] == request) { + found = YES; + break; + } + argIndex++; + } + + if (found) { + [authorizationQueue_ removeObjectAtIndex:argIndex]; + + // If the queue is now empty, go ahead and stop the fetcher. + if ([authorizationQueue_ count] == 0) { + [self stopAuthorization]; + } + } + } +} + +- (BOOL)authorizeRequestImmediateArgs:(GTMOAuth2AuthorizationArgs *)args { + // This authorization entry point never attempts to refresh the access token, + // but does call the completion routine + + NSMutableURLRequest *request = args.request; + + NSString *scheme = [[request URL] scheme]; + BOOL isAuthorizableRequest = self.shouldAuthorizeAllRequests + || [scheme caseInsensitiveCompare:@"https"] == NSOrderedSame; + if (!isAuthorizableRequest) { + // Request is not https, so may be insecure + // + // The NSError will be created below +#if DEBUG + NSLog(@"Cannot authorize request with scheme %@ (%@)", scheme, request); +#endif + } + + NSString *accessToken = self.accessToken; + if (isAuthorizableRequest && [accessToken length] > 0) { + if (request) { + // we have a likely valid access token + NSString *value = [NSString stringWithFormat:@"%s %@", + GTM_OAUTH2_BEARER, accessToken]; + [request setValue:value forHTTPHeaderField:@"Authorization"]; + } + + // We've authorized the request, even if the previous refresh + // failed with an error + args.error = nil; + } else if (args.error == nil) { + NSDictionary *userInfo = nil; + if (request) { + userInfo = [NSDictionary dictionaryWithObject:request + forKey:kGTMOAuth2ErrorRequestKey]; + } + NSInteger code = (isAuthorizableRequest ? + kGTMOAuth2ErrorAuthorizationFailed : + kGTMOAuth2ErrorUnauthorizableRequest); + args.error = [NSError errorWithDomain:kGTMOAuth2ErrorDomain + code:code + userInfo:userInfo]; + } + + // Invoke any callbacks on the proper thread + if (args.delegate || args.completionHandler) { + NSThread *targetThread = args.thread; + BOOL isSameThread = [targetThread isEqual:[NSThread currentThread]]; + + if (isSameThread) { + [self invokeCallbackArgs:args]; + } else { + SEL sel = @selector(invokeCallbackArgs:); + NSOperationQueue *delegateQueue = self.fetcherService.delegateQueue; + if (delegateQueue) { + NSInvocationOperation *op; + op = [[[NSInvocationOperation alloc] initWithTarget:self + selector:sel + object:args] autorelease]; + [delegateQueue addOperation:op]; + } else { + [self performSelector:sel + onThread:targetThread + withObject:args + waitUntilDone:NO]; + } + } + } + + BOOL didAuth = (args.error == nil); + return didAuth; +} + +- (void)invokeCallbackArgs:(GTMOAuth2AuthorizationArgs *)args { + // Invoke the callbacks + NSError *error = args.error; + + id delegate = args.delegate; + SEL sel = args.selector; + if (delegate && sel) { + NSMutableURLRequest *request = args.request; + + NSMethodSignature *sig = [delegate methodSignatureForSelector:sel]; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig]; + [invocation setSelector:sel]; + [invocation setTarget:delegate]; + [invocation setArgument:&self atIndex:2]; + [invocation setArgument:&request atIndex:3]; + [invocation setArgument:&error atIndex:4]; + [invocation invoke]; + } + +#if NS_BLOCKS_AVAILABLE + id handler = args.completionHandler; + if (handler) { + void (^authCompletionBlock)(NSError *) = handler; + authCompletionBlock(error); + } +#endif +} + +- (BOOL)authorizeRequest:(NSMutableURLRequest *)request { + // Entry point for synchronous authorization mechanisms + GTMOAuth2AuthorizationArgs *args; + args = [GTMOAuth2AuthorizationArgs argsWithRequest:request + delegate:nil + selector:NULL + completionHandler:nil + thread:[NSThread currentThread]]; + return [self authorizeRequestImmediateArgs:args]; +} + +- (BOOL)canAuthorize { + 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; + } + BOOL canAuth = [token length] > 0; + return canAuth; +} + +- (BOOL)shouldRefreshAccessToken { + // We should refresh the access token when it's missing or nearly expired + // and we have a refresh token + BOOL shouldRefresh = NO; + NSString *accessToken = self.accessToken; + NSString *refreshToken = self.refreshToken; + NSString *assertion = self.assertion; + NSString *code = self.code; + + BOOL hasRefreshToken = ([refreshToken length] > 0); + BOOL hasAccessToken = ([accessToken length] > 0); + BOOL hasAssertion = ([assertion length] > 0); + BOOL hasCode = ([code length] > 0); + + // Determine if we need to refresh the access token + if (hasRefreshToken || hasAssertion || hasCode) { + if (!hasAccessToken) { + shouldRefresh = YES; + } else { + // We'll consider the token expired if it expires 60 seconds from now + // or earlier + NSDate *expirationDate = self.expirationDate; + NSTimeInterval timeToExpire = [expirationDate timeIntervalSinceNow]; + if (expirationDate == nil || timeToExpire < 60.0) { + // access token has expired, or will in a few seconds + shouldRefresh = YES; + } + } + } + return shouldRefresh; +} + +- (void)waitForCompletionWithTimeout:(NSTimeInterval)timeoutInSeconds { + // If there is a refresh fetcher pending, wait for it. + // + // This is only intended for unit test or for use in command-line tools. + GTMHTTPFetcher *fetcher = self.refreshFetcher; + [fetcher waitForCompletionWithTimeout:timeoutInSeconds]; +} + +#pragma mark Token Fetch + +- (NSString *)userAgent { + NSBundle *bundle = [NSBundle mainBundle]; + NSString *appID = [bundle bundleIdentifier]; + + NSString *version = [bundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; + if (version == nil) { + version = [bundle objectForInfoDictionaryKey:@"CFBundleVersion"]; + } + + if (appID && version) { + appID = [appID stringByAppendingFormat:@"/%@", version]; + } + + NSString *userAgent = @"gtm-oauth2"; + if (appID) { + userAgent = [userAgent stringByAppendingFormat:@" %@", appID]; + } + return userAgent; +} + +- (GTMHTTPFetcher *)beginTokenFetchWithDelegate:(id)delegate + didFinishSelector:(SEL)finishedSel { + + NSMutableDictionary *paramsDict = [NSMutableDictionary dictionary]; + + NSString *fetchType; + + NSString *refreshToken = self.refreshToken; + NSString *code = self.code; + NSString *assertion = self.assertion; + + if (refreshToken) { + // We have a refresh token + [paramsDict setObject:@"refresh_token" forKey:@"grant_type"]; + [paramsDict setObject:refreshToken forKey:@"refresh_token"]; + + NSString *refreshScope = self.refreshScope; + if ([refreshScope length] > 0) { + [paramsDict setObject:refreshScope forKey:@"scope"]; + } + + fetchType = kGTMOAuth2FetchTypeRefresh; + } else if (code) { + // We have a code string + [paramsDict setObject:@"authorization_code" forKey:@"grant_type"]; + [paramsDict setObject:code forKey:@"code"]; + + NSString *redirectURI = self.redirectURI; + if ([redirectURI length] > 0) { + [paramsDict setObject:redirectURI forKey:@"redirect_uri"]; + } + + NSString *scope = self.scope; + if ([scope length] > 0) { + [paramsDict setObject:scope forKey:@"scope"]; + } + + fetchType = kGTMOAuth2FetchTypeToken; + } else if (assertion) { + // We have an assertion string + [paramsDict setObject:assertion forKey:@"assertion"]; + [paramsDict setObject:@"http://oauth.net/grant_type/jwt/1.0/bearer" + forKey:@"grant_type"]; + fetchType = kGTMOAuth2FetchTypeAssertion; + } else { +#if DEBUG + NSAssert(0, @"unexpected lack of code or refresh token for fetching"); +#endif + return nil; + } + + NSString *clientID = self.clientID; + if ([clientID length] > 0) { + [paramsDict setObject:clientID forKey:@"client_id"]; + } + + NSString *clientSecret = self.clientSecret; + if ([clientSecret length] > 0) { + [paramsDict setObject:clientSecret forKey:@"client_secret"]; + } + + NSDictionary *additionalParams = self.additionalTokenRequestParameters; + if (additionalParams) { + [paramsDict addEntriesFromDictionary:additionalParams]; + } + + NSString *paramStr = [[self class] encodedQueryParametersForDictionary:paramsDict]; + NSData *paramData = [paramStr dataUsingEncoding:NSUTF8StringEncoding]; + + NSURL *tokenURL = self.tokenURL; + + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:tokenURL]; + [request setValue:@"application/x-www-form-urlencoded" + forHTTPHeaderField:@"Content-Type"]; + + NSString *userAgent = [self userAgent]; + [request setValue:userAgent forHTTPHeaderField:@"User-Agent"]; + + GTMHTTPFetcher *fetcher; + id fetcherService = self.fetcherService; + if (fetcherService) { + fetcher = [fetcherService fetcherWithRequest:request]; + + // Don't use an authorizer for an auth token fetch + fetcher.authorizer = nil; + } else { + fetcher = [GTMHTTPFetcher fetcherWithRequest:request]; + } + + NSString *const template = (refreshToken ? @"refresh token for %@" : @"fetch tokens for %@"); + [fetcher setCommentWithFormat:template, [tokenURL host]]; + fetcher.postData = paramData; + fetcher.retryEnabled = YES; + fetcher.maxRetryInterval = 15.0; + + // Fetcher properties will retain the delegate + [fetcher setProperty:delegate forKey:kTokenFetchDelegateKey]; + if (finishedSel) { + NSString *selStr = NSStringFromSelector(finishedSel); + [fetcher setProperty:selStr forKey:kTokenFetchSelectorKey]; + } + + if ([fetcher beginFetchWithDelegate:self + didFinishSelector:@selector(tokenFetcher:finishedWithData:error:)]) { + // Fetch began + [self notifyFetchIsRunning:YES fetcher:fetcher type:fetchType]; + return fetcher; + } else { + // Failed to start fetching; typically a URL issue + NSError *error = [NSError errorWithDomain:kGTMHTTPFetcherStatusDomain + code:-1 + userInfo:nil]; + [[self class] invokeDelegate:delegate + selector:finishedSel + object:self + object:nil + object:error]; + return nil; + } +} + +- (void)tokenFetcher:(GTMHTTPFetcher *)fetcher + finishedWithData:(NSData *)data + error:(NSError *)error { + [self notifyFetchIsRunning:NO fetcher:fetcher type:nil]; + + NSDictionary *responseHeaders = [fetcher responseHeaders]; + NSString *responseType = [responseHeaders valueForKey:@"Content-Type"]; + BOOL isResponseJSON = [responseType hasPrefix:@"application/json"]; + 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 + if (hasData) { + if (isResponseJSON) { + NSDictionary *errorJson = [self dictionaryWithJSONData:data]; + if ([errorJson count] > 0) { +#if DEBUG + NSLog(@"Error %@\nError data:\n%@", error, errorJson); +#endif + // Add the JSON error body to the userInfo of the error + NSMutableDictionary *userInfo; + userInfo = [NSMutableDictionary dictionaryWithObject:errorJson + forKey:kGTMOAuth2ErrorJSONKey]; + NSDictionary *prevUserInfo = [error userInfo]; + if (prevUserInfo) { + [userInfo addEntriesFromDictionary:prevUserInfo]; + } + error = [NSError errorWithDomain:[error domain] + code:[error code] + userInfo:userInfo]; + } + } + } + } else { + // Succeeded; we have an access token +#if DEBUG + NSAssert(hasData, @"data missing in token response"); +#endif + + if (hasData) { + if (isResponseJSON) { + [self setKeysForResponseJSONData:data]; + } else { + // Support for legacy token servers that return form-urlencoded data + NSString *dataStr = [[[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding] autorelease]; + [self setKeysForResponseString:dataStr]; + } + +#if DEBUG + // Watch for token exchanges that return a non-bearer or unlabeled token + NSString *tokenType = [self tokenType]; + if (tokenType == nil + || [tokenType caseInsensitiveCompare:@"bearer"] != NSOrderedSame) { + NSLog(@"GTMOAuth2: Unexpected token type: %@", tokenType); + } +#endif + } + } + + id delegate = [fetcher propertyForKey:kTokenFetchDelegateKey]; + SEL sel = NULL; + NSString *selStr = [fetcher propertyForKey:kTokenFetchSelectorKey]; + if (selStr) sel = NSSelectorFromString(selStr); + + [[self class] invokeDelegate:delegate + selector:sel + object:self + object:fetcher + object:error]; + + // Prevent a circular reference from retaining the delegate + [fetcher setProperty:nil forKey:kTokenFetchDelegateKey]; +} + +#pragma mark Fetch Notifications + +- (void)notifyFetchIsRunning:(BOOL)isStarting + fetcher:(GTMHTTPFetcher *)fetcher + type:(NSString *)fetchType { + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + + NSString *name = (isStarting ? kGTMOAuth2FetchStarted : kGTMOAuth2FetchStopped); + NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys: + fetcher, kGTMOAuth2FetcherKey, + fetchType, kGTMOAuth2FetchTypeKey, // fetchType may be nil + nil]; + [nc postNotificationName:name + object:self + userInfo:dict]; +} + +#pragma mark Persistent Response Strings + +- (void)setKeysForPersistenceResponseString:(NSString *)str { + // All persistence keys can be set directly as if returned by a server + [self setKeysForResponseString:str]; +} + +// This returns a "response string" that can be passed later to +// setKeysForResponseString: to reuse an old access token in a new auth object +- (NSString *)persistenceResponseString { + NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:4]; + + NSString *refreshToken = self.refreshToken; + NSString *accessToken = nil; + if (refreshToken == nil) { + // We store the access token only for services that do not support refresh + // tokens; otherwise, we assume the access token is too perishable to + // be worth storing + accessToken = self.accessToken; + } + + // Any nil values will not set a dictionary entry + [dict setValue:refreshToken forKey:kOAuth2RefreshTokenKey]; + [dict setValue:accessToken forKey:kOAuth2AccessTokenKey]; + [dict setValue:self.serviceProvider forKey:kServiceProviderKey]; + [dict setValue:self.userID forKey:kUserIDKey]; + [dict setValue:self.userEmail forKey:kUserEmailKey]; + [dict setValue:self.userEmailIsVerified forKey:kUserEmailIsVerifiedKey]; + [dict setValue:self.scope forKey:kOAuth2ScopeKey]; + + NSString *result = [[self class] encodedQueryParametersForDictionary:dict]; + return result; +} + +- (BOOL)primeForRefresh { + if (self.refreshToken == nil) { + // Cannot refresh without a refresh token + return NO; + } + self.accessToken = nil; + self.expiresIn = nil; + self.expirationDate = nil; + self.errorString = nil; + return YES; +} + +- (void)reset { + // Reset all per-authorization values + self.code = nil; + self.accessToken = nil; + self.refreshToken = nil; + self.assertion = nil; + self.expiresIn = nil; + self.errorString = nil; + self.expirationDate = nil; + self.userEmail = nil; + self.userEmailIsVerified = nil; +} + +#pragma mark Accessors for Response Parameters + +- (NSString *)accessToken { + return [self.parameters objectForKey:kOAuth2AccessTokenKey]; +} + +- (void)setAccessToken:(NSString *)str { + [self.parameters setValue:str forKey:kOAuth2AccessTokenKey]; +} + +- (NSString *)refreshToken { + return [self.parameters objectForKey:kOAuth2RefreshTokenKey]; +} + +- (void)setRefreshToken:(NSString *)str { + [self.parameters setValue:str forKey:kOAuth2RefreshTokenKey]; +} + +- (NSString *)code { + return [self.parameters objectForKey:kOAuth2CodeKey]; +} + +- (void)setCode:(NSString *)str { + [self.parameters setValue:str forKey:kOAuth2CodeKey]; +} + +- (NSString *)assertion { + return [self.parameters objectForKey:kOAuth2AssertionKey]; +} + +- (void)setAssertion:(NSString *)str { + [self.parameters setValue:str forKey:kOAuth2AssertionKey]; +} + +- (NSString *)refreshScope { + return [self.parameters objectForKey:kOAuth2RefreshScopeKey]; +} + +- (void)setRefreshScope:(NSString *)str { + [self.parameters setValue:str forKey:kOAuth2RefreshScopeKey]; +} + +- (NSString *)errorString { + return [self.parameters objectForKey:kOAuth2ErrorKey]; +} + +- (void)setErrorString:(NSString *)str { + [self.parameters setValue:str forKey:kOAuth2ErrorKey]; +} + +- (NSString *)tokenType { + return [self.parameters objectForKey:kOAuth2TokenTypeKey]; +} + +- (void)setTokenType:(NSString *)str { + [self.parameters setValue:str forKey:kOAuth2TokenTypeKey]; +} + +- (NSString *)scope { + return [self.parameters objectForKey:kOAuth2ScopeKey]; +} + +- (void)setScope:(NSString *)str { + [self.parameters setValue:str forKey:kOAuth2ScopeKey]; +} + +- (NSNumber *)expiresIn { + return [self.parameters objectForKey:kOAuth2ExpiresInKey]; +} + +- (void)setExpiresIn:(NSNumber *)num { + [self.parameters setValue:num forKey:kOAuth2ExpiresInKey]; + [self updateExpirationDate]; +} + +- (void)updateExpirationDate { + // Update our absolute expiration time to something close to when + // the server expects the expiration + NSDate *date = nil; + NSNumber *expiresIn = self.expiresIn; + if (expiresIn) { + unsigned long deltaSeconds = [expiresIn unsignedLongValue]; + if (deltaSeconds > 0) { + date = [NSDate dateWithTimeIntervalSinceNow:deltaSeconds]; + } + } + self.expirationDate = date; +} + +// +// Keys custom to this class, not part of OAuth 2 +// + +- (NSString *)serviceProvider { + return [self.parameters objectForKey:kServiceProviderKey]; +} + +- (void)setServiceProvider:(NSString *)str { + [self.parameters setValue:str forKey:kServiceProviderKey]; +} + +- (NSString *)userID { + return [self.parameters objectForKey:kUserIDKey]; +} + +- (void)setUserID:(NSString *)str { + [self.parameters setValue:str forKey:kUserIDKey]; +} + +- (NSString *)userEmail { + return [self.parameters objectForKey:kUserEmailKey]; +} + +- (void)setUserEmail:(NSString *)str { + [self.parameters setValue:str forKey:kUserEmailKey]; +} + +- (NSString *)userEmailIsVerified { + return [self.parameters objectForKey:kUserEmailIsVerifiedKey]; +} + +- (void)setUserEmailIsVerified:(NSString *)str { + [self.parameters setValue:str forKey:kUserEmailIsVerifiedKey]; +} + +#pragma mark User Properties + +- (void)setProperty:(id)obj forKey:(NSString *)key { + if (obj == nil) { + // User passed in nil, so delete the property + [properties_ removeObjectForKey:key]; + } else { + // Be sure the property dictionary exists + if (properties_ == nil) { + [self setProperties:[NSMutableDictionary dictionary]]; + } + [properties_ setObject:obj forKey:key]; + } +} + +- (id)propertyForKey:(NSString *)key { + id obj = [properties_ objectForKey:key]; + + // Be sure the returned pointer has the life of the autorelease pool, + // in case self is released immediately + return [[obj retain] autorelease]; +} + +#pragma mark Utility Routines + ++ (NSString *)encodedOAuthValueForString:(NSString *)str { + CFStringRef originalString = (CFStringRef) str; + CFStringRef leaveUnescaped = NULL; + CFStringRef forceEscaped = CFSTR("!*'();:@&=+$,/?%#[]"); + + CFStringRef escapedStr = NULL; + if (str) { + escapedStr = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, + originalString, + leaveUnescaped, + forceEscaped, + kCFStringEncodingUTF8); + [(id)CFMakeCollectable(escapedStr) autorelease]; + } + + return (NSString *)escapedStr; +} + ++ (NSString *)encodedQueryParametersForDictionary:(NSDictionary *)dict { + // Make a string like "cat=fluffy@dog=spot" + NSMutableString *result = [NSMutableString string]; + NSArray *sortedKeys = [[dict allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]; + NSString *joiner = @""; + for (NSString *key in sortedKeys) { + NSString *value = [dict objectForKey:key]; + NSString *encodedValue = [self encodedOAuthValueForString:value]; + NSString *encodedKey = [self encodedOAuthValueForString:key]; + [result appendFormat:@"%@%@=%@", joiner, encodedKey, encodedValue]; + joiner = @"&"; + } + return result; +} + ++ (void)invokeDelegate:(id)delegate + selector:(SEL)sel + object:(id)obj1 + object:(id)obj2 + object:(id)obj3 { + if (delegate && sel) { + NSMethodSignature *sig = [delegate methodSignatureForSelector:sel]; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig]; + [invocation setSelector:sel]; + [invocation setTarget:delegate]; + [invocation setArgument:&obj1 atIndex:2]; + [invocation setArgument:&obj2 atIndex:3]; + [invocation setArgument:&obj3 atIndex:4]; + [invocation invoke]; + } +} + ++ (NSString *)unencodedOAuthParameterForString:(NSString *)str { + NSString *plainStr = [str stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + return plainStr; +} + ++ (NSDictionary *)dictionaryWithResponseString:(NSString *)responseStr { + // Build a dictionary from a response string of the form + // "cat=fluffy&dog=spot". Missing or empty values are considered + // empty strings; keys and values are percent-decoded. + if (responseStr == nil) return nil; + + NSArray *items = [responseStr componentsSeparatedByString:@"&"]; + + NSMutableDictionary *responseDict = [NSMutableDictionary dictionaryWithCapacity:[items count]]; + + for (NSString *item in items) { + NSString *key = nil; + NSString *value = @""; + + NSRange equalsRange = [item rangeOfString:@"="]; + if (equalsRange.location != NSNotFound) { + // The parameter has at least one '=' + key = [item substringToIndex:equalsRange.location]; + + // There are characters after the '=' + value = [item substringFromIndex:(equalsRange.location + 1)]; + } else { + // The parameter has no '=' + key = item; + } + + NSString *plainKey = [[self class] unencodedOAuthParameterForString:key]; + NSString *plainValue = [[self class] unencodedOAuthParameterForString:value]; + + [responseDict setObject:plainValue forKey:plainKey]; + } + + return responseDict; +} + ++ (NSDictionary *)dictionaryWithResponseData:(NSData *)data { + NSString *responseStr = [[[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding] autorelease]; + NSDictionary *dict = [self dictionaryWithResponseString:responseStr]; + return dict; +} + ++ (NSString *)scopeWithStrings:(NSString *)str, ... { + // concatenate the strings, joined by a single space + NSString *result = @""; + NSString *joiner = @""; + if (str) { + va_list argList; + va_start(argList, str); + while (str) { + result = [result stringByAppendingFormat:@"%@%@", joiner, str]; + joiner = @" "; + str = va_arg(argList, id); + } + va_end(argList); + } + return result; +} + +@end + +#endif // GTM_INCLUDE_OAUTH2 || !GDATA_REQUIRE_SERVICE_INCLUDES diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTMOAuth2SignIn.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTMOAuth2SignIn.h new file mode 100644 index 0000000000..ded279bd23 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTMOAuth2SignIn.h @@ -0,0 +1,187 @@ +/* Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// This sign-in object opens and closes the web view window as needed for +// users to sign in. For signing in to Google, it also obtains +// the authenticated user's email address. +// +// Typically, this will be managed for the application by +// GTMOAuth2ViewControllerTouch or GTMOAuth2WindowController, so this +// class's interface is interesting only if +// you are creating your own window controller for sign-in. +// +// +// Delegate methods implemented by the window controller +// +// The window controller implements two methods for use by the sign-in object, +// the webRequestSelector and the finishedSelector: +// +// webRequestSelector has a signature matching +// - (void)signIn:(GTMOAuth2SignIn *)signIn displayRequest:(NSURLRequest *)request +// +// The web request selector will be invoked with a request to be displayed, or +// nil to close the window when the final callback request has been encountered. +// +// +// finishedSelector has a signature matching +// - (void)signin:(GTMOAuth2SignIn *)signin finishedWithAuth:(GTMOAuth2Authentication *)auth error:(NSError *)error +// +// The finished selector will be invoked when sign-in has completed, except +// when explicitly canceled by calling cancelSigningIn +// + +#if GTM_INCLUDE_OAUTH2 || !GDATA_REQUIRE_SERVICE_INCLUDES + +#import +#import + +// GTMHTTPFetcher brings in GTLDefines/GDataDefines +#import "GTMHTTPFetcher.h" + +#import "GTMOAuth2Authentication.h" + +@interface GTMOAuth2SignIn : NSObject { + @private + GTMOAuth2Authentication *auth_; + + // the endpoint for displaying the sign-in page + NSURL *authorizationURL_; + NSDictionary *additionalAuthorizationParameters_; + + id delegate_; + SEL webRequestSelector_; + SEL finishedSelector_; + + BOOL hasHandledCallback_; + + GTMHTTPFetcher *pendingFetcher_; + +#if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT + BOOL shouldFetchGoogleUserEmail_; + BOOL shouldFetchGoogleUserProfile_; + NSDictionary *userProfile_; +#endif + + SCNetworkReachabilityRef reachabilityRef_; + NSTimer *networkLossTimer_; + NSTimeInterval networkLossTimeoutInterval_; + BOOL hasNotifiedNetworkLoss_; + + id userData_; +} + +@property (nonatomic, retain) GTMOAuth2Authentication *authentication; + +@property (nonatomic, retain) NSURL *authorizationURL; +@property (nonatomic, retain) NSDictionary *additionalAuthorizationParameters; + +// The delegate is released when signing in finishes or is cancelled +@property (nonatomic, retain) id delegate; +@property (nonatomic, assign) SEL webRequestSelector; +@property (nonatomic, assign) SEL finishedSelector; + +@property (nonatomic, retain) id userData; + +// By default, signing in to Google will fetch the user's email, but will not +// fetch the user's profile. +// +// The email is saved in the auth object. +// The profile is available immediately after sign-in. +#if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT +@property (nonatomic, assign) BOOL shouldFetchGoogleUserEmail; +@property (nonatomic, assign) BOOL shouldFetchGoogleUserProfile; +@property (nonatomic, retain, readonly) NSDictionary *userProfile; +#endif + +// The default timeout for an unreachable network during display of the +// sign-in page is 30 seconds; set this to 0 to have no timeout +@property (nonatomic, assign) NSTimeInterval networkLossTimeoutInterval; + +// The delegate is retained until sign-in has completed or been canceled +// +// designated initializer +- (id)initWithAuthentication:(GTMOAuth2Authentication *)auth + authorizationURL:(NSURL *)authorizationURL + delegate:(id)delegate + webRequestSelector:(SEL)webRequestSelector + finishedSelector:(SEL)finishedSelector; + +// A default authentication object for signing in to Google services +#if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT ++ (GTMOAuth2Authentication *)standardGoogleAuthenticationForScope:(NSString *)scope + clientID:(NSString *)clientID + clientSecret:(NSString *)clientSecret; +#endif + +#pragma mark Methods used by the Window Controller + +// Start the sequence of fetches and sign-in window display for sign-in +- (BOOL)startSigningIn; + +// Stop any pending fetches, and close the window (but don't call the +// delegate's finishedSelector) +- (void)cancelSigningIn; + +// Window controllers must tell the sign-in object about any redirect +// requested by the web view, and any changes in the webview window title +// +// If these return YES then the event was handled by the +// sign-in object (typically by closing the window) and should be ignored by +// the window controller's web view + +- (BOOL)requestRedirectedToRequest:(NSURLRequest *)redirectedRequest; +- (BOOL)titleChanged:(NSString *)title; +- (BOOL)cookiesChanged:(NSHTTPCookieStorage *)cookieStorage; +- (BOOL)loadFailedWithError:(NSError *)error; + +// Window controllers must tell the sign-in object if the window was closed +// prematurely by the user (but not by the sign-in object); this calls the +// delegate's finishedSelector +- (void)windowWasClosed; + +// Start the sequences for signing in with an authorization code. The +// authentication must contain an authorization code, otherwise the process +// will fail. +- (void)authCodeObtained; + +#pragma mark - + +#if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT +// Revocation of an authorized token from Google ++ (void)revokeTokenForGoogleAuthentication:(GTMOAuth2Authentication *)auth; + +// Create a fetcher for obtaining the user's Google email address or profile, +// according to the current auth scopes. +// +// The auth object must have been created with appropriate scopes. +// +// The fetcher's response data can be parsed with NSJSONSerialization. ++ (GTMHTTPFetcher *)userInfoFetcherWithAuth:(GTMOAuth2Authentication *)auth; +#endif + +#pragma mark - + +// Standard authentication values ++ (NSString *)nativeClientRedirectURI; +#if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT ++ (NSURL *)googleAuthorizationURL; ++ (NSURL *)googleTokenURL; ++ (NSURL *)googleUserInfoURL; +#endif + +@end + +#endif // #if GTM_INCLUDE_OAUTH2 || !GDATA_REQUIRE_SERVICE_INCLUDES diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTMOAuth2SignIn.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTMOAuth2SignIn.m new file mode 100644 index 0000000000..fba0222ff1 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTMOAuth2SignIn.m @@ -0,0 +1,835 @@ +/* Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if GTM_INCLUDE_OAUTH2 || !GDATA_REQUIRE_SERVICE_INCLUDES + +#define GTMOAUTH2SIGNIN_DEFINE_GLOBALS 1 +#import "GTMOAuth2SignIn.h" + +// we'll default to timing out if the network becomes unreachable for more +// than 30 seconds when the sign-in page is displayed +static const NSTimeInterval kDefaultNetworkLossTimeoutInterval = 30.0; + +// URI indicating an installed app is signing in. This is described at +// +// http://code.google.com/apis/accounts/docs/OAuth2.html#IA +// +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; +#if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT +@property (nonatomic, retain, readwrite) NSDictionary *userProfile; +#endif + +- (void)invokeFinalCallbackWithError:(NSError *)error; + +- (BOOL)startWebRequest; ++ (NSMutableURLRequest *)mutableURLRequestWithURL:(NSURL *)oldURL + paramString:(NSString *)paramStr; +#if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT +- (void)addScopeForGoogleUserInfo; +- (void)fetchGoogleUserInfo; +#endif +- (void)finishSignInWithError:(NSError *)error; + +- (void)auth:(GTMOAuth2Authentication *)auth +finishedWithFetcher:(GTMHTTPFetcher *)fetcher + error:(NSError *)error; + +#if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT +- (void)infoFetcher:(GTMHTTPFetcher *)fetcher + finishedWithData:(NSData *)data + error:(NSError *)error; +#endif + +- (void)closeTheWindow; + +- (void)startReachabilityCheck; +- (void)stopReachabilityCheck; +- (void)reachabilityTarget:(SCNetworkReachabilityRef)reachabilityRef + changedFlags:(SCNetworkConnectionFlags)flags; +- (void)reachabilityTimerFired:(NSTimer *)timer; +@end + +@implementation GTMOAuth2SignIn + +@synthesize authentication = auth_; + +@synthesize authorizationURL = authorizationURL_; +@synthesize additionalAuthorizationParameters = additionalAuthorizationParameters_; + +@synthesize delegate = delegate_; +@synthesize webRequestSelector = webRequestSelector_; +@synthesize finishedSelector = finishedSelector_; +@synthesize hasHandledCallback = hasHandledCallback_; +@synthesize pendingFetcher = pendingFetcher_; +@synthesize userData = userData_; + +#if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT +@synthesize shouldFetchGoogleUserEmail = shouldFetchGoogleUserEmail_; +@synthesize shouldFetchGoogleUserProfile = shouldFetchGoogleUserProfile_; +@synthesize userProfile = userProfile_; +#endif + +@synthesize networkLossTimeoutInterval = networkLossTimeoutInterval_; + +#if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT ++ (NSURL *)googleAuthorizationURL { + NSString *str = @"https://accounts.google.com/o/oauth2/auth"; + return [NSURL URLWithString:str]; +} + ++ (NSURL *)googleTokenURL { + NSString *str = @"https://accounts.google.com/o/oauth2/token"; + return [NSURL URLWithString:str]; +} + ++ (NSURL *)googleRevocationURL { + NSString *urlStr = @"https://accounts.google.com/o/oauth2/revoke"; + return [NSURL URLWithString:urlStr]; +} + ++ (NSURL *)googleUserInfoURL { + NSString *urlStr = @"https://www.googleapis.com/oauth2/v1/userinfo"; + return [NSURL URLWithString:urlStr]; +} +#endif + ++ (NSString *)nativeClientRedirectURI { + return kOOBString; +} + +#if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT ++ (GTMOAuth2Authentication *)standardGoogleAuthenticationForScope:(NSString *)scope + clientID:(NSString *)clientID + clientSecret:(NSString *)clientSecret { + NSString *redirectURI = [self nativeClientRedirectURI]; + NSURL *tokenURL = [self googleTokenURL]; + + GTMOAuth2Authentication *auth; + auth = [GTMOAuth2Authentication authenticationWithServiceProvider:kGTMOAuth2ServiceProviderGoogle + tokenURL:tokenURL + redirectURI:redirectURI + clientID:clientID + clientSecret:clientSecret]; + auth.scope = scope; + + return auth; +} + +- (void)addScopeForGoogleUserInfo { + GTMOAuth2Authentication *auth = self.authentication; + if (self.shouldFetchGoogleUserEmail) { + NSString *const emailScope = @"https://www.googleapis.com/auth/userinfo.email"; + NSString *scope = auth.scope; + if ([scope rangeOfString:emailScope].location == NSNotFound) { + scope = [GTMOAuth2Authentication scopeWithStrings:scope, emailScope, nil]; + auth.scope = scope; + } + } + + if (self.shouldFetchGoogleUserProfile) { + NSString *const profileScope = @"https://www.googleapis.com/auth/userinfo.profile"; + NSString *scope = auth.scope; + if ([scope rangeOfString:profileScope].location == NSNotFound) { + scope = [GTMOAuth2Authentication scopeWithStrings:scope, profileScope, nil]; + auth.scope = scope; + } + } +} +#endif + +- (id)initWithAuthentication:(GTMOAuth2Authentication *)auth + authorizationURL:(NSURL *)authorizationURL + delegate:(id)delegate + webRequestSelector:(SEL)webRequestSelector + finishedSelector:(SEL)finishedSelector { + // check the selectors on debug builds + GTMAssertSelectorNilOrImplementedWithArgs(delegate, webRequestSelector, + @encode(GTMOAuth2SignIn *), @encode(NSURLRequest *), 0); + GTMAssertSelectorNilOrImplementedWithArgs(delegate, finishedSelector, + @encode(GTMOAuth2SignIn *), @encode(GTMOAuth2Authentication *), + @encode(NSError *), 0); + + // designated initializer + self = [super init]; + if (self) { + auth_ = [auth retain]; + authorizationURL_ = [authorizationURL retain]; + delegate_ = [delegate retain]; + webRequestSelector_ = webRequestSelector; + finishedSelector_ = finishedSelector; + + // for Google authentication, we want to automatically fetch user info +#if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT + NSString *host = [authorizationURL host]; + if ([host hasSuffix:@".google.com"]) { + shouldFetchGoogleUserEmail_ = YES; + } +#endif + + // default timeout for a lost internet connection while the server + // UI is displayed is 30 seconds + networkLossTimeoutInterval_ = kDefaultNetworkLossTimeoutInterval; + } + return self; +} + +- (void)dealloc { + [self stopReachabilityCheck]; + + [auth_ release]; + [authorizationURL_ release]; + [additionalAuthorizationParameters_ release]; + [delegate_ release]; + [pendingFetcher_ release]; +#if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT + [userProfile_ release]; +#endif + [userData_ release]; + + [super dealloc]; +} + +#pragma mark Sign-in Sequence Methods + +// stop any pending fetches, and close the window (but don't call the +// delegate's finishedSelector) +- (void)cancelSigningIn { + [self.pendingFetcher stopFetching]; + self.pendingFetcher = nil; + + [self.authentication stopAuthorization]; + + [self closeTheWindow]; + + [delegate_ autorelease]; + delegate_ = nil; +} + +// +// This is the entry point to begin the sequence +// - display the authentication web page, and monitor redirects +// - exchange the code for an access token and a refresh token +// - for Google sign-in, fetch the user's email address +// - tell the delegate we're finished +// +- (BOOL)startSigningIn { + // For signing in to Google, append the scope for obtaining the authenticated + // user email and profile, as appropriate +#if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT + [self addScopeForGoogleUserInfo]; +#endif + + // start the authorization + return [self startWebRequest]; +} + +- (NSMutableDictionary *)parametersForWebRequest { + GTMOAuth2Authentication *auth = self.authentication; + NSString *clientID = auth.clientID; + NSString *redirectURI = auth.redirectURI; + + BOOL hasClientID = ([clientID length] > 0); + BOOL hasRedirect = ([redirectURI length] > 0 + || redirectURI == [[self class] nativeClientRedirectURI]); + if (!hasClientID || !hasRedirect) { +#if DEBUG + NSAssert(hasClientID, @"GTMOAuth2SignIn: clientID needed"); + NSAssert(hasRedirect, @"GTMOAuth2SignIn: redirectURI needed"); +#endif + return NO; + } + + // invoke the UI controller's web request selector to display + // the authorization page + + // add params to the authorization URL + NSString *scope = auth.scope; + if ([scope length] == 0) scope = nil; + + NSMutableDictionary *paramsDict = [NSMutableDictionary dictionaryWithObjectsAndKeys: + @"code", @"response_type", + clientID, @"client_id", + scope, @"scope", // scope may be nil + nil]; + if (redirectURI) { + [paramsDict setObject:redirectURI forKey:@"redirect_uri"]; + } + return paramsDict; +} + +- (BOOL)startWebRequest { + NSMutableDictionary *paramsDict = [self parametersForWebRequest]; + + NSDictionary *additionalParams = self.additionalAuthorizationParameters; + if (additionalParams) { + [paramsDict addEntriesFromDictionary:additionalParams]; + } + + NSString *paramStr = [GTMOAuth2Authentication encodedQueryParametersForDictionary:paramsDict]; + + NSURL *authorizationURL = self.authorizationURL; + NSMutableURLRequest *request; + request = [[self class] mutableURLRequestWithURL:authorizationURL + paramString:paramStr]; + + [delegate_ performSelector:self.webRequestSelector + withObject:self + withObject:request]; + + // at this point, we're waiting on the server-driven html UI, so + // we want notification if we lose connectivity to the web server + [self startReachabilityCheck]; + return YES; +} + +// utility for making a request from an old URL with some additional parameters ++ (NSMutableURLRequest *)mutableURLRequestWithURL:(NSURL *)oldURL + paramString:(NSString *)paramStr { + NSString *query = [oldURL query]; + if ([query length] > 0) { + query = [query stringByAppendingFormat:@"&%@", paramStr]; + } else { + query = paramStr; + } + + NSString *portStr = @""; + NSString *oldPort = [[oldURL port] stringValue]; + if ([oldPort length] > 0) { + portStr = [@":" stringByAppendingString:oldPort]; + } + + NSString *qMark = [query length] > 0 ? @"?" : @""; + NSString *newURLStr = [NSString stringWithFormat:@"%@://%@%@%@%@%@", + [oldURL scheme], [oldURL host], portStr, + [oldURL path], qMark, query]; + NSURL *newURL = [NSURL URLWithString:newURLStr]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:newURL]; + return request; +} + +// entry point for the window controller to tell us that the window +// prematurely closed +- (void)windowWasClosed { + [self stopReachabilityCheck]; + + NSError *error = [NSError errorWithDomain:kGTMOAuth2ErrorDomain + code:kGTMOAuth2ErrorWindowClosed + userInfo:nil]; + [self invokeFinalCallbackWithError:error]; +} + +// internal method to tell the window controller to close the window +- (void)closeTheWindow { + [self stopReachabilityCheck]; + + // a nil request means the window should be closed + [delegate_ performSelector:self.webRequestSelector + withObject:self + withObject:nil]; +} + +// entry point for the window controller to tell us what web page has been +// requested +// +// When the request is for the callback URL, this method invokes +// authCodeObtained and returns YES +- (BOOL)requestRedirectedToRequest:(NSURLRequest *)redirectedRequest { + // for Google's installed app sign-in protocol, we'll look for the + // end-of-sign-in indicator in the titleChanged: method below + NSString *redirectURI = self.authentication.redirectURI; + if (redirectURI == nil) return NO; + + // when we're searching for the window title string, then we can ignore + // redirects + NSString *standardURI = [[self class] nativeClientRedirectURI]; + if (standardURI != nil && [redirectURI isEqual:standardURI]) return NO; + + // compare the redirectURI, which tells us when the web sign-in is done, + // to the actual redirection + NSURL *redirectURL = [NSURL URLWithString:redirectURI]; + NSURL *requestURL = [redirectedRequest URL]; + + // avoid comparing to nil host and path values (such as when redirected to + // "about:blank") + NSString *requestHost = [requestURL host]; + NSString *requestPath = [requestURL path]; + BOOL isCallback; + if (requestHost && requestPath) { + isCallback = [[redirectURL host] isEqual:[requestURL host]] + && [[redirectURL path] isEqual:[requestURL path]]; + } else if (requestURL) { + // handle "about:blank" + isCallback = [redirectURL isEqual:requestURL]; + } else { + isCallback = NO; + } + + if (!isCallback) { + // tell the caller that this request is nothing interesting + return NO; + } + + // we've reached the callback URL + + // try to get the access code + if (!self.hasHandledCallback) { + NSString *responseStr = [[redirectedRequest URL] query]; + [self.authentication setKeysForResponseString:responseStr]; + +#if DEBUG + NSAssert([self.authentication.code length] > 0 + || [self.authentication.errorString length] > 0, + @"response lacks auth code or error"); +#endif + + [self authCodeObtained]; + } + // tell the delegate that we did handle this request + return YES; +} + +// entry point for the window controller to tell us when a new page title has +// been loadded +// +// When the title indicates sign-in has completed, this method invokes +// authCodeObtained and returns YES +- (BOOL)titleChanged:(NSString *)title { + // return YES if the OAuth flow ending title was detected + + // right now we're just looking for a parameter list following the last space + // in the title string, but hopefully we'll eventually get something better + // from the server to search for + NSRange paramsRange = [title rangeOfString:@" " + options:NSBackwardsSearch]; + NSUInteger spaceIndex = paramsRange.location; + if (spaceIndex != NSNotFound) { + NSString *responseStr = [title substringFromIndex:(spaceIndex + 1)]; + + NSDictionary *dict = [GTMOAuth2Authentication dictionaryWithResponseString:responseStr]; + + NSString *code = [dict objectForKey:@"code"]; + NSString *error = [dict objectForKey:@"error"]; + if ([code length] > 0 || [error length] > 0) { + + if (!self.hasHandledCallback) { + [self.authentication setKeysForResponseDictionary:dict]; + + [self authCodeObtained]; + } + return YES; + } + } + return NO; +} + +- (BOOL)cookiesChanged:(NSHTTPCookieStorage *)cookieStorage { + // We're ignoring these. + return NO; +}; + +// entry point for the window controller to tell us when a load has failed +// in the webview +// +// if the initial authorization URL fails, bail out so the user doesn't +// see an empty webview +- (BOOL)loadFailedWithError:(NSError *)error { + NSURL *authorizationURL = self.authorizationURL; + NSURL *failedURL = [[error userInfo] valueForKey:@"NSErrorFailingURLKey"]; // NSURLErrorFailingURLErrorKey defined in 10.6 + + BOOL isAuthURL = [[failedURL host] isEqual:[authorizationURL host]] + && [[failedURL path] isEqual:[authorizationURL path]]; + + if (isAuthURL) { + // We can assume that we have no pending fetchers, since we only + // handle failure to load the initial authorization URL + [self closeTheWindow]; + [self invokeFinalCallbackWithError:error]; + return YES; + } + return NO; +} + +- (void)authCodeObtained { + // the callback page was requested, or the authenticate code was loaded + // into a page's title, so exchange the auth code for access & refresh tokens + // and tell the window to close + + // avoid duplicate signals that the callback point has been reached + self.hasHandledCallback = YES; + + // If the signin was request for exchanging an authentication token to a + // refresh token, there is no window to close. + if (self.webRequestSelector) { + [self closeTheWindow]; + } else { + // For signing in to Google, append the scope for obtaining the + // authenticated user email and profile, as appropriate. This is usually + // done by the startSigningIn method, but this method is not called when + // exchanging an authentication token for a refresh token. +#if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT + [self addScopeForGoogleUserInfo]; +#endif + } + + NSError *error = nil; + + GTMOAuth2Authentication *auth = self.authentication; + NSString *code = auth.code; + if ([code length] > 0) { + // exchange the code for a token + SEL sel = @selector(auth:finishedWithFetcher:error:); + GTMHTTPFetcher *fetcher = [auth beginTokenFetchWithDelegate:self + didFinishSelector:sel]; + if (fetcher == nil) { + error = [NSError errorWithDomain:kGTMHTTPFetcherStatusDomain + code:-1 + userInfo:nil]; + } else { + self.pendingFetcher = fetcher; + } + + // notify the app so it can put up a post-sign in, pre-token exchange UI + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc postNotificationName:kGTMOAuth2UserSignedIn + object:self + userInfo:nil]; + } else { + // the callback lacked an auth code + NSString *errStr = auth.errorString; + NSDictionary *userInfo = nil; + if ([errStr length] > 0) { + userInfo = [NSDictionary dictionaryWithObject:errStr + forKey:kGTMOAuth2ErrorMessageKey]; + } + + error = [NSError errorWithDomain:kGTMOAuth2ErrorDomain + code:kGTMOAuth2ErrorAuthorizationFailed + userInfo:userInfo]; + } + + if (error) { + [self finishSignInWithError:error]; + } +} + +- (void)auth:(GTMOAuth2Authentication *)auth +finishedWithFetcher:(GTMHTTPFetcher *)fetcher + error:(NSError *)error { + self.pendingFetcher = nil; + +#if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT + if (error == nil + && (self.shouldFetchGoogleUserEmail || self.shouldFetchGoogleUserProfile) + && [self.authentication.serviceProvider isEqual:kGTMOAuth2ServiceProviderGoogle]) { + // fetch the user's information from the Google server + [self fetchGoogleUserInfo]; + } else { + // we're not authorizing with Google, so we're done + [self finishSignInWithError:error]; + } +#else + [self finishSignInWithError:error]; +#endif +} + +#if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT ++ (GTMHTTPFetcher *)userInfoFetcherWithAuth:(GTMOAuth2Authentication *)auth { + // create a fetcher for obtaining the user's email or profile + NSURL *infoURL = [[self class] googleUserInfoURL]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:infoURL]; + + NSString *userAgent = [auth userAgent]; + [request setValue:userAgent forHTTPHeaderField:@"User-Agent"]; + [request setValue:@"no-cache" forHTTPHeaderField:@"Cache-Control"]; + + GTMHTTPFetcher *fetcher; + id fetcherService = auth.fetcherService; + if (fetcherService) { + fetcher = [fetcherService fetcherWithRequest:request]; + } else { + fetcher = [GTMHTTPFetcher fetcherWithRequest:request]; + } + fetcher.authorizer = auth; + fetcher.retryEnabled = YES; + fetcher.maxRetryInterval = 15.0; + fetcher.comment = @"user info"; + return fetcher; +} + +- (void)fetchGoogleUserInfo { + // fetch the user's email address or profile + GTMOAuth2Authentication *auth = self.authentication; + GTMHTTPFetcher *fetcher = [[self class] userInfoFetcherWithAuth:auth]; + [fetcher beginFetchWithDelegate:self + didFinishSelector:@selector(infoFetcher:finishedWithData:error:)]; + + self.pendingFetcher = fetcher; + + [auth notifyFetchIsRunning:YES + fetcher:fetcher + type:kGTMOAuth2FetchTypeUserInfo]; +} + +- (void)infoFetcher:(GTMHTTPFetcher *)fetcher + finishedWithData:(NSData *)data + error:(NSError *)error { + GTMOAuth2Authentication *auth = self.authentication; + [auth notifyFetchIsRunning:NO + fetcher:fetcher + type:nil]; + + self.pendingFetcher = nil; + + if (error) { +#if DEBUG + if (data) { + NSString *dataStr = [[[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding] autorelease]; + NSLog(@"infoFetcher error: %@\n%@", error, dataStr); + } +#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 finishSignInWithError:error]; +} + +#endif // !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT + +- (void)finishSignInWithError:(NSError *)error { + [self invokeFinalCallbackWithError:error]; +} + +// convenience method for making the final call to our delegate +- (void)invokeFinalCallbackWithError:(NSError *)error { + if (delegate_ && finishedSelector_) { + GTMOAuth2Authentication *auth = self.authentication; + + NSMethodSignature *sig = [delegate_ methodSignatureForSelector:finishedSelector_]; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig]; + [invocation setSelector:finishedSelector_]; + [invocation setTarget:delegate_]; + [invocation setArgument:&self atIndex:2]; + [invocation setArgument:&auth atIndex:3]; + [invocation setArgument:&error atIndex:4]; + [invocation invoke]; + } + + // we'll no longer send messages to the delegate + // + // we want to autorelease it rather than assign to the property in case + // the delegate is below us in the call stack + [delegate_ autorelease]; + delegate_ = nil; +} + +#pragma mark Reachability monitoring + +static void ReachabilityCallBack(SCNetworkReachabilityRef target, + SCNetworkConnectionFlags flags, + void *info) { + // pass the flags to the signIn object + GTMOAuth2SignIn *signIn = (GTMOAuth2SignIn *)info; + + [signIn reachabilityTarget:target + changedFlags:flags]; +} + +- (void)startReachabilityCheck { + // the user may set the timeout to 0 to skip the reachability checking + // during display of the sign-in page + if (networkLossTimeoutInterval_ <= 0.0 || reachabilityRef_ != NULL) { + return; + } + + // create a reachability target from the authorization URL, add our callback, + // and schedule it on the run loop so we'll be notified if the network drops + NSURL *url = self.authorizationURL; + const char* host = [[url host] UTF8String]; + reachabilityRef_ = SCNetworkReachabilityCreateWithName(kCFAllocatorSystemDefault, + host); + if (reachabilityRef_) { + BOOL isScheduled = NO; + SCNetworkReachabilityContext ctx = { 0, self, NULL, NULL, NULL }; + + if (SCNetworkReachabilitySetCallback(reachabilityRef_, + ReachabilityCallBack, &ctx)) { + if (SCNetworkReachabilityScheduleWithRunLoop(reachabilityRef_, + CFRunLoopGetCurrent(), + kCFRunLoopDefaultMode)) { + isScheduled = YES; + } + } + + if (!isScheduled) { + CFRelease(reachabilityRef_); + reachabilityRef_ = NULL; + } + } +} + +- (void)destroyUnreachabilityTimer { + [networkLossTimer_ invalidate]; + [networkLossTimer_ autorelease]; + networkLossTimer_ = nil; +} + +- (void)reachabilityTarget:(SCNetworkReachabilityRef)reachabilityRef + changedFlags:(SCNetworkConnectionFlags)flags { + BOOL isConnected = (flags & kSCNetworkFlagsReachable) != 0 + && (flags & kSCNetworkFlagsConnectionRequired) == 0; + + if (isConnected) { + // server is again reachable + [self destroyUnreachabilityTimer]; + + if (hasNotifiedNetworkLoss_) { + // tell the user that the network has been found + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc postNotificationName:kGTMOAuth2NetworkFound + object:self + userInfo:nil]; + hasNotifiedNetworkLoss_ = NO; + } + } else { + // the server has become unreachable; start the timer, if necessary + if (networkLossTimer_ == nil + && networkLossTimeoutInterval_ > 0 + && !hasNotifiedNetworkLoss_) { + SEL sel = @selector(reachabilityTimerFired:); + networkLossTimer_ = [[NSTimer scheduledTimerWithTimeInterval:networkLossTimeoutInterval_ + target:self + selector:sel + userInfo:nil + repeats:NO] retain]; + } + } +} + +- (void)reachabilityTimerFired:(NSTimer *)timer { + // the user may call [[notification object] cancelSigningIn] to + // dismiss the sign-in + if (!hasNotifiedNetworkLoss_) { + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc postNotificationName:kGTMOAuth2NetworkLost + object:self + userInfo:nil]; + hasNotifiedNetworkLoss_ = YES; + } + + [self destroyUnreachabilityTimer]; +} + +- (void)stopReachabilityCheck { + [self destroyUnreachabilityTimer]; + + if (reachabilityRef_) { + SCNetworkReachabilityUnscheduleFromRunLoop(reachabilityRef_, + CFRunLoopGetCurrent(), + kCFRunLoopDefaultMode); + SCNetworkReachabilitySetCallback(reachabilityRef_, NULL, NULL); + + CFRelease(reachabilityRef_); + reachabilityRef_ = NULL; + } +} + +#pragma mark Token Revocation + +#if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT ++ (void)revokeTokenForGoogleAuthentication:(GTMOAuth2Authentication *)auth { + if (auth.refreshToken != nil + && auth.canAuthorize + && [auth.serviceProvider isEqual:kGTMOAuth2ServiceProviderGoogle]) { + + // create a signed revocation request for this authentication object + NSURL *url = [self googleRevocationURL]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; + [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"]; + + NSString *token = auth.refreshToken; + NSString *encoded = [GTMOAuth2Authentication encodedOAuthValueForString:token]; + if (encoded != nil) { + NSString *body = [@"token=" stringByAppendingString:encoded]; + + [request setHTTPBody:[body dataUsingEncoding:NSUTF8StringEncoding]]; + [request setHTTPMethod:@"POST"]; + + NSString *userAgent = [auth userAgent]; + [request setValue:userAgent forHTTPHeaderField:@"User-Agent"]; + + // there's nothing to be done if revocation succeeds or fails + GTMHTTPFetcher *fetcher; + id fetcherService = auth.fetcherService; + if (fetcherService) { + fetcher = [fetcherService fetcherWithRequest:request]; + } else { + fetcher = [GTMHTTPFetcher fetcherWithRequest:request]; + } + fetcher.comment = @"revoke token"; + + // Use a completion handler fetch for better debugging, but only if we're + // guaranteed that blocks are available in the runtime +#if (!TARGET_OS_IPHONE && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1060)) || \ + (TARGET_OS_IPHONE && (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000)) + // Blocks are available + [fetcher beginFetchWithCompletionHandler:^(NSData *data, NSError *error) { + #if DEBUG + if (error) { + NSString *errStr = [[[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding] autorelease]; + NSLog(@"revoke error: %@", errStr); + } + #endif // DEBUG + }]; +#else + // Blocks may not be available + [fetcher beginFetchWithDelegate:nil didFinishSelector:NULL]; +#endif + } + } + [auth reset]; +} +#endif // !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT + +@end + +#endif // #if GTM_INCLUDE_OAUTH2 || !GDATA_REQUIRE_SERVICE_INCLUDES diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTMOAuth2ViewControllerTouch.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTMOAuth2ViewControllerTouch.h new file mode 100644 index 0000000000..d20b33075d --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTMOAuth2ViewControllerTouch.h @@ -0,0 +1,361 @@ +/* Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTMOAuth2ViewControllerTouch.h +// +// This view controller for iPhone handles sign-in via OAuth to Google or +// other services. +// +// This controller is not reusable; create a new instance of this controller +// every time the user will sign in. +// + +#if GTM_INCLUDE_OAUTH2 || !GDATA_REQUIRE_SERVICE_INCLUDES + +#import + +#if TARGET_OS_IPHONE + +#import + +#import "GTMOAuth2Authentication.h" + +#undef _EXTERN +#undef _INITIALIZE_AS +#ifdef GTMOAUTH2VIEWCONTROLLERTOUCH_DEFINE_GLOBALS +#define _EXTERN +#define _INITIALIZE_AS(x) =x +#else +#define _EXTERN extern +#define _INITIALIZE_AS(x) +#endif + +_EXTERN NSString* const kGTMOAuth2KeychainErrorDomain _INITIALIZE_AS(@"com.google.GTMOAuthKeychain"); + + +@class GTMOAuth2SignIn; +@class GTMOAuth2ViewControllerTouch; + +@interface GTMOAuth2ViewControllerTouch : UIViewController { + @private + UIButton *backButton_; + UIButton *forwardButton_; + UIActivityIndicatorView *initialActivityIndicator_; + UIView *navButtonsView_; + UIBarButtonItem *rightBarButtonItem_; + UIWebView *webView_; + + // The object responsible for the sign-in networking sequence; it holds + // onto the authentication object as well. + GTMOAuth2SignIn *signIn_; + + // the page request to load when awakeFromNib occurs + NSURLRequest *request_; + + // The user we're calling back + // + // The delegate is retained only until the callback is invoked + // or the sign-in is canceled + id delegate_; + SEL finishedSelector_; + +#if NS_BLOCKS_AVAILABLE + void (^completionBlock_)(GTMOAuth2ViewControllerTouch *, GTMOAuth2Authentication *, NSError *); + + void (^popViewBlock_)(void); +#endif + + NSString *keychainItemName_; + CFTypeRef keychainItemAccessibility_; + + // if non-nil, the html string to be displayed immediately upon opening + // of the web view + NSString *initialHTMLString_; + + // set to 1 or -1 if the user sets the showsInitialActivityIndicator + // property + int mustShowActivityIndicator_; + + // if non-nil, the URL for which cookies will be deleted when the + // browser view is dismissed + NSURL *browserCookiesURL_; + + id userData_; + NSMutableDictionary *properties_; + +#if __IPHONE_OS_VERSION_MIN_REQUIRED < 60000 + // We delegate the decision to our owning NavigationController (if any). + // But, the NavigationController will call us back, and ask us. + // BOOL keeps us from infinite looping. + BOOL isInsideShouldAutorotateToInterfaceOrientation_; +#endif + + // YES, when view first shown in this signIn session. + BOOL isViewShown_; + + // YES, after the view has fully transitioned in. + BOOL didViewAppear_; + + // YES between sends of start and stop notifications + BOOL hasNotifiedWebViewStartedLoading_; + + // To prevent us from calling our delegate's selector more than once. + BOOL hasCalledFinished_; + + // Set in a webView callback. + BOOL hasDoneFinalRedirect_; + + // Set during the pop initiated by the sign-in object; otherwise, + // viewWillDisappear indicates that some external change of the view + // has stopped the sign-in. + BOOL didDismissSelf_; +} + +// the application and service name to use for saving the auth tokens +// to the keychain +@property (nonatomic, copy) NSString *keychainItemName; + +// the keychain item accessibility is a system constant for use +// with kSecAttrAccessible. +// +// Since it's a system constant, we do not need to retain it. +@property (nonatomic, assign) CFTypeRef keychainItemAccessibility; + +// optional html string displayed immediately upon opening the web view +// +// This string is visible just until the sign-in web page loads, and +// may be used for a "Loading..." type of message or to set the +// initial view color +@property (nonatomic, copy) NSString *initialHTMLString; + +// an activity indicator shows during initial webview load when no initial HTML +// string is specified, but the activity indicator can be forced to be shown +// with this property +@property (nonatomic, assign) BOOL showsInitialActivityIndicator; + +// the underlying object to hold authentication tokens and authorize http +// requests +@property (nonatomic, retain, readonly) GTMOAuth2Authentication *authentication; + +// the underlying object which performs the sign-in networking sequence +@property (nonatomic, retain, readonly) GTMOAuth2SignIn *signIn; + +// user interface elements +@property (nonatomic, retain) IBOutlet UIButton *backButton; +@property (nonatomic, retain) IBOutlet UIButton *forwardButton; +@property (nonatomic, retain) IBOutlet UIActivityIndicatorView *initialActivityIndicator; +@property (nonatomic, retain) IBOutlet UIView *navButtonsView; +@property (nonatomic, retain) IBOutlet UIBarButtonItem *rightBarButtonItem; +@property (nonatomic, retain) IBOutlet UIWebView *webView; + +#if NS_BLOCKS_AVAILABLE +// An optional block to be called when the view should be popped. If not set, +// the view controller will use its navigation controller to pop the view. +@property (nonatomic, copy) void (^popViewBlock)(void); +#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 +@property (nonatomic, assign) NSTimeInterval networkLossTimeoutInterval; + +// if set, cookies are deleted for this URL when the view is hidden +// +// For Google sign-ins, this is set by default to https://google.com/accounts +// but it may be explicitly set to nil to disable clearing of browser cookies +@property (nonatomic, retain) NSURL *browserCookiesURL; + +// userData is retained for the convenience of the caller +@property (nonatomic, retain) id userData; + +// Stored property values are retained for the convenience of the caller +- (void)setProperty:(id)obj forKey:(NSString *)key; +- (id)propertyForKey:(NSString *)key; + +@property (nonatomic, retain) NSDictionary *properties; + +// Method for creating a controller to authenticate to Google services +// +// scope is the requested scope of authorization +// (like "http://www.google.com/m8/feeds") +// +// keychain item name is used for storing the token on the keychain, +// keychainItemName should be like "My Application: Google Latitude" +// (or set to nil if no persistent keychain storage is desired) +// +// the delegate is retained only until the finished selector is invoked +// or the sign-in is canceled +// +// If you don't like the default nibName and bundle, you can change them +// using the UIViewController properties once you've made one of these. +// +// finishedSelector is called after authentication completes. It should follow +// this signature. +// +// - (void)viewController:(GTMOAuth2ViewControllerTouch *)viewController +// finishedWithAuth:(GTMOAuth2Authentication *)auth +// error:(NSError *)error; +// +#if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT ++ (id)controllerWithScope:(NSString *)scope + clientID:(NSString *)clientID + clientSecret:(NSString *)clientSecret + keychainItemName:(NSString *)keychainItemName + delegate:(id)delegate + finishedSelector:(SEL)finishedSelector; + +- (id)initWithScope:(NSString *)scope + clientID:(NSString *)clientID + clientSecret:(NSString *)clientSecret + keychainItemName:(NSString *)keychainItemName + delegate:(id)delegate + finishedSelector:(SEL)finishedSelector; + +#if NS_BLOCKS_AVAILABLE ++ (id)controllerWithScope:(NSString *)scope + clientID:(NSString *)clientID + clientSecret:(NSString *)clientSecret + keychainItemName:(NSString *)keychainItemName + completionHandler:(void (^)(GTMOAuth2ViewControllerTouch *viewController, GTMOAuth2Authentication *auth, NSError *error))handler; + +- (id)initWithScope:(NSString *)scope + clientID:(NSString *)clientID + clientSecret:(NSString *)clientSecret + keychainItemName:(NSString *)keychainItemName + completionHandler:(void (^)(GTMOAuth2ViewControllerTouch *viewController, GTMOAuth2Authentication *auth, NSError *error))handler; +#endif +#endif + +// Create a controller for authenticating to non-Google services, taking +// explicit endpoint URLs and an authentication object ++ (id)controllerWithAuthentication:(GTMOAuth2Authentication *)auth + authorizationURL:(NSURL *)authorizationURL + keychainItemName:(NSString *)keychainItemName // may be nil + delegate:(id)delegate + finishedSelector:(SEL)finishedSelector; + +// This is the designated initializer +- (id)initWithAuthentication:(GTMOAuth2Authentication *)auth + authorizationURL:(NSURL *)authorizationURL + keychainItemName:(NSString *)keychainItemName + delegate:(id)delegate + finishedSelector:(SEL)finishedSelector; + +#if NS_BLOCKS_AVAILABLE ++ (id)controllerWithAuthentication:(GTMOAuth2Authentication *)auth + authorizationURL:(NSURL *)authorizationURL + keychainItemName:(NSString *)keychainItemName // may be nil + completionHandler:(void (^)(GTMOAuth2ViewControllerTouch *viewController, GTMOAuth2Authentication *auth, NSError *error))handler; + +- (id)initWithAuthentication:(GTMOAuth2Authentication *)auth + authorizationURL:(NSURL *)authorizationURL + keychainItemName:(NSString *)keychainItemName + completionHandler:(void (^)(GTMOAuth2ViewControllerTouch *viewController, GTMOAuth2Authentication *auth, NSError *error))handler; +#endif + +// subclasses may override authNibName to specify a custom name ++ (NSString *)authNibName; + +// subclasses may override authNibBundle to specify a custom bundle ++ (NSBundle *)authNibBundle; + +// apps may replace the sign-in class with their own subclass of it ++ (Class)signInClass; ++ (void)setSignInClass:(Class)theClass; + +- (void)cancelSigningIn; + +// revocation of an authorized token from Google +#if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT ++ (void)revokeTokenForGoogleAuthentication:(GTMOAuth2Authentication *)auth; +#endif + +// +// Keychain +// + +// create an authentication object for Google services from the access +// token and secret stored in the keychain; if no token is available, return +// an unauthorized auth object +#if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT ++ (GTMOAuth2Authentication *)authForGoogleFromKeychainForName:(NSString *)keychainItemName + clientID:(NSString *)clientID + clientSecret:(NSString *)clientSecret; +#endif + +// add tokens from the keychain, if available, to the authentication object +// +// returns YES if the authentication object was authorized from the keychain ++ (BOOL)authorizeFromKeychainForName:(NSString *)keychainItemName + authentication:(GTMOAuth2Authentication *)auth; + +// method for deleting the stored access token and secret, useful for "signing +// out" ++ (BOOL)removeAuthFromKeychainForName:(NSString *)keychainItemName; + +// method for saving the stored access token and secret ++ (BOOL)saveParamsToKeychainForName:(NSString *)keychainItemName + accessibility:(CFTypeRef)accessibility + authentication:(GTMOAuth2Authentication *)auth; + +// older version, defaults to kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly ++ (BOOL)saveParamsToKeychainForName:(NSString *)keychainItemName + authentication:(GTMOAuth2Authentication *)auth; + +@end + +// To function, GTMOAuth2ViewControllerTouch needs a certain amount of access +// to the iPhone's keychain. To keep things simple, its keychain access is +// broken out into a helper class. We declare it here in case you'd like to use +// it too, to store passwords. + +enum { + kGTMOAuth2KeychainErrorBadArguments = -1301, + kGTMOAuth2KeychainErrorNoPassword = -1302 +}; + + +@interface GTMOAuth2Keychain : NSObject + ++ (GTMOAuth2Keychain *)defaultKeychain; + +// OK to pass nil for the error parameter. +- (NSString *)passwordForService:(NSString *)service + account:(NSString *)account + error:(NSError **)error; + +// OK to pass nil for the error parameter. +- (BOOL)removePasswordForService:(NSString *)service + account:(NSString *)account + error:(NSError **)error; + +// OK to pass nil for the error parameter. +// +// accessibility should be one of the constants for kSecAttrAccessible +// such as kSecAttrAccessibleWhenUnlocked +- (BOOL)setPassword:(NSString *)password + forService:(NSString *)service + accessibility:(CFTypeRef)accessibility + account:(NSString *)account + error:(NSError **)error; + +// For unit tests: allow setting a mock object ++ (void)setDefaultKeychain:(GTMOAuth2Keychain *)keychain; + +@end + +#endif // TARGET_OS_IPHONE + +#endif // #if GTM_INCLUDE_OAUTH2 || !GDATA_REQUIRE_SERVICE_INCLUDES diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTMOAuth2ViewControllerTouch.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTMOAuth2ViewControllerTouch.m new file mode 100644 index 0000000000..40cf4aeeb3 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTMOAuth2ViewControllerTouch.m @@ -0,0 +1,1035 @@ +/* Copyright (c) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// GTMOAuth2ViewControllerTouch.m +// + +#import +#import + +#if GTM_INCLUDE_OAUTH2 || !GDATA_REQUIRE_SERVICE_INCLUDES + +#if TARGET_OS_IPHONE + +#define GTMOAUTH2VIEWCONTROLLERTOUCH_DEFINE_GLOBALS 1 +#import "GTMOAuth2ViewControllerTouch.h" + +#import "GTMOAuth2SignIn.h" +#import "GTMOAuth2Authentication.h" + +static NSString * const kGTMOAuth2AccountName = @"OAuth"; +static GTMOAuth2Keychain* sDefaultKeychain = nil; + +@interface GTMOAuth2ViewControllerTouch() + +@property (nonatomic, copy) NSURLRequest *request; + +- (void)signIn:(GTMOAuth2SignIn *)signIn displayRequest:(NSURLRequest *)request; +- (void)signIn:(GTMOAuth2SignIn *)signIn +finishedWithAuth:(GTMOAuth2Authentication *)auth + error:(NSError *)error; +- (BOOL)isNavigationBarTranslucent; +- (void)moveWebViewFromUnderNavigationBar; +- (void)popView; +- (void)clearBrowserCookies; +@end + +@implementation GTMOAuth2ViewControllerTouch + +// IBOutlets +@synthesize request = request_, + backButton = backButton_, + forwardButton = forwardButton_, + navButtonsView = navButtonsView_, + rightBarButtonItem = rightBarButtonItem_, + webView = webView_, + initialActivityIndicator = initialActivityIndicator_; + +@synthesize keychainItemName = keychainItemName_, + keychainItemAccessibility = keychainItemAccessibility_, + initialHTMLString = initialHTMLString_, + browserCookiesURL = browserCookiesURL_, + signIn = signIn_, + userData = userData_, + properties = properties_; + +#if NS_BLOCKS_AVAILABLE +@synthesize popViewBlock = popViewBlock_; +#endif + +#if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT ++ (id)controllerWithScope:(NSString *)scope + clientID:(NSString *)clientID + clientSecret:(NSString *)clientSecret + keychainItemName:(NSString *)keychainItemName + delegate:(id)delegate + finishedSelector:(SEL)finishedSelector { + return [[[self alloc] initWithScope:scope + clientID:clientID + clientSecret:clientSecret + keychainItemName:keychainItemName + delegate:delegate + finishedSelector:finishedSelector] autorelease]; +} + +- (id)initWithScope:(NSString *)scope + clientID:(NSString *)clientID + clientSecret:(NSString *)clientSecret + keychainItemName:(NSString *)keychainItemName + delegate:(id)delegate + finishedSelector:(SEL)finishedSelector { + // convenient entry point for Google authentication + + Class signInClass = [[self class] signInClass]; + + GTMOAuth2Authentication *auth; + auth = [signInClass standardGoogleAuthenticationForScope:scope + clientID:clientID + clientSecret:clientSecret]; + NSURL *authorizationURL = [signInClass googleAuthorizationURL]; + return [self initWithAuthentication:auth + authorizationURL:authorizationURL + keychainItemName:keychainItemName + delegate:delegate + finishedSelector:finishedSelector]; +} + +#if NS_BLOCKS_AVAILABLE + ++ (id)controllerWithScope:(NSString *)scope + clientID:(NSString *)clientID + clientSecret:(NSString *)clientSecret + keychainItemName:(NSString *)keychainItemName + completionHandler:(void (^)(GTMOAuth2ViewControllerTouch *viewController, GTMOAuth2Authentication *auth, NSError *error))handler { + return [[[self alloc] initWithScope:scope + clientID:clientID + clientSecret:clientSecret + keychainItemName:keychainItemName + completionHandler:handler] autorelease]; +} + +- (id)initWithScope:(NSString *)scope + clientID:(NSString *)clientID + clientSecret:(NSString *)clientSecret + keychainItemName:(NSString *)keychainItemName + completionHandler:(void (^)(GTMOAuth2ViewControllerTouch *viewController, GTMOAuth2Authentication *auth, NSError *error))handler { + // convenient entry point for Google authentication + + Class signInClass = [[self class] signInClass]; + + GTMOAuth2Authentication *auth; + auth = [signInClass standardGoogleAuthenticationForScope:scope + clientID:clientID + clientSecret:clientSecret]; + NSURL *authorizationURL = [signInClass googleAuthorizationURL]; + self = [self initWithAuthentication:auth + authorizationURL:authorizationURL + keychainItemName:keychainItemName + delegate:nil + finishedSelector:NULL]; + if (self) { + completionBlock_ = [handler copy]; + } + return self; +} + +#endif // NS_BLOCKS_AVAILABLE +#endif // !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT + ++ (id)controllerWithAuthentication:(GTMOAuth2Authentication *)auth + authorizationURL:(NSURL *)authorizationURL + keychainItemName:(NSString *)keychainItemName + delegate:(id)delegate + finishedSelector:(SEL)finishedSelector { + return [[[self alloc] initWithAuthentication:auth + authorizationURL:authorizationURL + keychainItemName:keychainItemName + delegate:delegate + finishedSelector:finishedSelector] autorelease]; +} + +- (id)initWithAuthentication:(GTMOAuth2Authentication *)auth + authorizationURL:(NSURL *)authorizationURL + keychainItemName:(NSString *)keychainItemName + delegate:(id)delegate + finishedSelector:(SEL)finishedSelector { + + NSString *nibName = [[self class] authNibName]; + NSBundle *nibBundle = [[self class] authNibBundle]; + + self = [super initWithNibName:nibName bundle:nibBundle]; + if (self != nil) { + delegate_ = [delegate retain]; + finishedSelector_ = finishedSelector; + + Class signInClass = [[self class] signInClass]; + + // use the supplied auth and OAuth endpoint URLs + signIn_ = [[signInClass alloc] initWithAuthentication:auth + authorizationURL:authorizationURL + delegate:self + webRequestSelector:@selector(signIn:displayRequest:) + finishedSelector:@selector(signIn:finishedWithAuth:error:)]; + + // if the user is signing in to a Google service, we'll delete the + // Google authentication browser cookies upon completion + // + // for other service domains, or to disable clearing of the cookies, + // set the browserCookiesURL property explicitly + NSString *authorizationHost = [signIn_.authorizationURL host]; + if ([authorizationHost hasSuffix:@".google.com"]) { + NSString *urlStr = [NSString stringWithFormat:@"https://%@/", + authorizationHost]; + NSURL *cookiesURL = [NSURL URLWithString:urlStr]; + [self setBrowserCookiesURL:cookiesURL]; + } + + [self setKeychainItemName:keychainItemName]; + } + return self; +} + +#if NS_BLOCKS_AVAILABLE ++ (id)controllerWithAuthentication:(GTMOAuth2Authentication *)auth + authorizationURL:(NSURL *)authorizationURL + keychainItemName:(NSString *)keychainItemName + completionHandler:(void (^)(GTMOAuth2ViewControllerTouch *viewController, GTMOAuth2Authentication *auth, NSError *error))handler { + return [[[self alloc] initWithAuthentication:auth + authorizationURL:authorizationURL + keychainItemName:keychainItemName + completionHandler:handler] autorelease]; +} + +- (id)initWithAuthentication:(GTMOAuth2Authentication *)auth + authorizationURL:(NSURL *)authorizationURL + keychainItemName:(NSString *)keychainItemName + completionHandler:(void (^)(GTMOAuth2ViewControllerTouch *viewController, GTMOAuth2Authentication *auth, NSError *error))handler { + // fall back to the non-blocks init + self = [self initWithAuthentication:auth + authorizationURL:authorizationURL + keychainItemName:keychainItemName + delegate:nil + finishedSelector:NULL]; + if (self) { + completionBlock_ = [handler copy]; + } + return self; +} +#endif + +- (void)dealloc { + [webView_ setDelegate:nil]; + + [backButton_ release]; + [forwardButton_ release]; + [initialActivityIndicator_ release]; + [navButtonsView_ release]; + [rightBarButtonItem_ release]; + [webView_ release]; + [signIn_ release]; + [request_ release]; + [delegate_ release]; +#if NS_BLOCKS_AVAILABLE + [completionBlock_ release]; + [popViewBlock_ release]; +#endif + [keychainItemName_ release]; + [initialHTMLString_ release]; + [browserCookiesURL_ release]; + [userData_ release]; + [properties_ release]; + + [super dealloc]; +} + ++ (NSString *)authNibName { + // subclasses may override this to specify a custom nib name + return @"GTMOAuth2ViewTouch"; +} + ++ (NSBundle *)authNibBundle { + // subclasses may override this to specify a custom nib bundle + return nil; +} + +#if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT ++ (GTMOAuth2Authentication *)authForGoogleFromKeychainForName:(NSString *)keychainItemName + clientID:(NSString *)clientID + clientSecret:(NSString *)clientSecret { + Class signInClass = [self signInClass]; + NSURL *tokenURL = [signInClass googleTokenURL]; + NSString *redirectURI = [signInClass nativeClientRedirectURI]; + + GTMOAuth2Authentication *auth; + auth = [GTMOAuth2Authentication authenticationWithServiceProvider:kGTMOAuth2ServiceProviderGoogle + tokenURL:tokenURL + redirectURI:redirectURI + clientID:clientID + clientSecret:clientSecret]; + [[self class] authorizeFromKeychainForName:keychainItemName + authentication:auth]; + return auth; +} +#endif + ++ (BOOL)authorizeFromKeychainForName:(NSString *)keychainItemName + authentication:(GTMOAuth2Authentication *)newAuth { + newAuth.accessToken = nil; + + BOOL didGetTokens = NO; + GTMOAuth2Keychain *keychain = [GTMOAuth2Keychain defaultKeychain]; + NSString *password = [keychain passwordForService:keychainItemName + account:kGTMOAuth2AccountName + error:nil]; + if (password != nil) { + [newAuth setKeysForResponseString:password]; + didGetTokens = YES; + } + return didGetTokens; +} + ++ (BOOL)removeAuthFromKeychainForName:(NSString *)keychainItemName { + GTMOAuth2Keychain *keychain = [GTMOAuth2Keychain defaultKeychain]; + return [keychain removePasswordForService:keychainItemName + account:kGTMOAuth2AccountName + error:nil]; +} + ++ (BOOL)saveParamsToKeychainForName:(NSString *)keychainItemName + authentication:(GTMOAuth2Authentication *)auth { + return [self saveParamsToKeychainForName:keychainItemName + accessibility:NULL + authentication:auth]; +} + ++ (BOOL)saveParamsToKeychainForName:(NSString *)keychainItemName + accessibility:(CFTypeRef)accessibility + authentication:(GTMOAuth2Authentication *)auth { + [self removeAuthFromKeychainForName:keychainItemName]; + // don't save unless we have a token that can really authorize requests + if (![auth canAuthorize]) return NO; + + if (accessibility == NULL + && &kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly != NULL) { + accessibility = kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly; + } + + // make a response string containing the values we want to save + NSString *password = [auth persistenceResponseString]; + GTMOAuth2Keychain *keychain = [GTMOAuth2Keychain defaultKeychain]; + return [keychain setPassword:password + forService:keychainItemName + accessibility:accessibility + account:kGTMOAuth2AccountName + error:nil]; +} + +- (void)loadView { + NSString *nibPath = nil; + NSBundle *nibBundle = [self nibBundle]; + if (nibBundle == nil) { + nibBundle = [NSBundle mainBundle]; + } + NSString *nibName = self.nibName; + if (nibName != nil) { + nibPath = [nibBundle pathForResource:nibName ofType:@"nib"]; + } + if (nibPath != nil && [[NSFileManager defaultManager] fileExistsAtPath:nibPath]) { + [super loadView]; + } else { + // One of the requirements of loadView is that a valid view object is set to + // self.view upon completion. Otherwise, subclasses that attempt to + // access self.view after calling [super loadView] will enter an infinite + // loop due to the fact that UIViewController's -view accessor calls + // loadView when self.view is nil. + self.view = [[[UIView alloc] init] autorelease]; + +#if DEBUG + NSLog(@"missing %@.nib", nibName); +#endif + } +} + + +- (void)viewDidLoad { + rightBarButtonItem_.customView = navButtonsView_; + self.navigationItem.rightBarButtonItem = rightBarButtonItem_; +} + +- (void)popView { +#if NS_BLOCKS_AVAILABLE + void (^popViewBlock)() = self.popViewBlock; +#else + id popViewBlock = nil; +#endif + + if (popViewBlock || self.navigationController.topViewController == self) { + if (!self.view.hidden) { + // Set the flag to our viewWillDisappear method so it knows + // this is a disappearance initiated by the sign-in object, + // not the user cancelling via the navigation controller + didDismissSelf_ = YES; + + if (popViewBlock) { +#if NS_BLOCKS_AVAILABLE + popViewBlock(); + self.popViewBlock = nil; +#endif + } else { + [self.navigationController popViewControllerAnimated:YES]; + } + self.view.hidden = YES; + } + } +} + +- (void)notifyWithName:(NSString *)name + webView:(UIWebView *)webView + kind:(NSString *)kind { + BOOL isStarting = [name isEqual:kGTMOAuth2WebViewStartedLoading]; + if (hasNotifiedWebViewStartedLoading_ == isStarting) { + // Duplicate notification + // + // UIWebView's delegate methods are so unbalanced that there's little + // point trying to keep a count, as it could easily end up stuck greater + // than zero. + // + // We don't really have a way to track the starts and stops of + // subframe loads, too, as the webView in the notification is always + // for the topmost request. + return; + } + hasNotifiedWebViewStartedLoading_ = isStarting; + + // Notification for webview load starting and stopping + NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys: + webView, kGTMOAuth2WebViewKey, + kind, kGTMOAuth2WebViewStopKindKey, // kind may be nil + nil]; + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc postNotificationName:name + object:self + userInfo:dict]; +} + +- (void)cancelSigningIn { + // The application has explicitly asked us to cancel signing in + // (so no further callback is required) + hasCalledFinished_ = YES; + + [delegate_ autorelease]; + delegate_ = nil; + +#if NS_BLOCKS_AVAILABLE + [completionBlock_ autorelease]; + completionBlock_ = nil; +#endif + + // The sign-in object's cancel method will close the window + [signIn_ cancelSigningIn]; + hasDoneFinalRedirect_ = YES; +} + +static Class gSignInClass = Nil; + ++ (Class)signInClass { + if (gSignInClass == Nil) { + gSignInClass = [GTMOAuth2SignIn class]; + } + return gSignInClass; +} + ++ (void)setSignInClass:(Class)theClass { + gSignInClass = theClass; +} + +#pragma mark Token Revocation + +#if !GTM_OAUTH2_SKIP_GOOGLE_SUPPORT ++ (void)revokeTokenForGoogleAuthentication:(GTMOAuth2Authentication *)auth { + [[self signInClass] revokeTokenForGoogleAuthentication:auth]; +} +#endif + +#pragma mark Browser Cookies + +- (GTMOAuth2Authentication *)authentication { + return self.signIn.authentication; +} + +- (void)clearBrowserCookies { + // if browserCookiesURL is non-nil, then get cookies for that URL + // and delete them from the common application cookie storage + NSURL *cookiesURL = [self browserCookiesURL]; + if (cookiesURL) { + NSHTTPCookieStorage *cookieStorage; + + cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; + NSArray *cookies = [cookieStorage cookiesForURL:cookiesURL]; + + for (NSHTTPCookie *cookie in cookies) { + [cookieStorage deleteCookie:cookie]; + } + } +} + +#pragma mark Accessors + +- (void)setNetworkLossTimeoutInterval:(NSTimeInterval)val { + signIn_.networkLossTimeoutInterval = val; +} + +- (NSTimeInterval)networkLossTimeoutInterval { + return signIn_.networkLossTimeoutInterval; +} + +- (BOOL)shouldUseKeychain { + NSString *name = self.keychainItemName; + return ([name length] > 0); +} + +- (BOOL)showsInitialActivityIndicator { + return (mustShowActivityIndicator_ == 1 || initialHTMLString_ == nil); +} + +- (void)setShowsInitialActivityIndicator:(BOOL)flag { + mustShowActivityIndicator_ = (flag ? 1 : -1); +} + +#pragma mark User Properties + +- (void)setProperty:(id)obj forKey:(NSString *)key { + if (obj == nil) { + // User passed in nil, so delete the property + [properties_ removeObjectForKey:key]; + } else { + // Be sure the property dictionary exists + if (properties_ == nil) { + [self setProperties:[NSMutableDictionary dictionary]]; + } + [properties_ setObject:obj forKey:key]; + } +} + +- (id)propertyForKey:(NSString *)key { + id obj = [properties_ objectForKey:key]; + + // Be sure the returned pointer has the life of the autorelease pool, + // in case self is released immediately + return [[obj retain] autorelease]; +} + +#pragma mark SignIn callbacks + +- (void)signIn:(GTMOAuth2SignIn *)signIn displayRequest:(NSURLRequest *)request { + // This is the signIn object's webRequest method, telling the controller + // to either display the request in the webview, or if the request is nil, + // to close the window. + // + // All web requests and all window closing goes through this routine + +#if DEBUG + if (self.navigationController) { + if (self.navigationController.topViewController != self && request != nil) { + NSLog(@"Unexpected: Request to show, when already on top. request %@", [request URL]); + } else if(self.navigationController.topViewController != self && request == nil) { + NSLog(@"Unexpected: Request to pop, when not on top. request nil"); + } + } +#endif + + if (request != nil) { + const NSTimeInterval kJanuary2011 = 1293840000; + BOOL isDateValid = ([[NSDate date] timeIntervalSince1970] > kJanuary2011); + if (isDateValid) { + // Display the request. + self.request = request; + // The app may prefer some html other than blank white to be displayed + // before the sign-in web page loads. + // The first fetch might be slow, so the client programmer may want + // to show a local "loading" message. + // On iOS 5+, UIWebView will ignore loadHTMLString: if it's followed by + // a loadRequest: call, so if there is a "loading" message we defer + // the loadRequest: until after after we've drawn the "loading" message. + // + // If there is no initial html string, we show the activity indicator + // unless the user set showsInitialActivityIndicator to NO; if there + // is an initial html string, we hide the indicator unless the user set + // showsInitialActivityIndicator to YES. + NSString *html = self.initialHTMLString; + if ([html length] > 0) { + [initialActivityIndicator_ setHidden:(mustShowActivityIndicator_ < 1)]; + [self.webView loadHTMLString:html baseURL:nil]; + } else { + [initialActivityIndicator_ setHidden:(mustShowActivityIndicator_ < 0)]; + [self.webView loadRequest:request]; + } + } else { + // clock date is invalid, so signing in would fail with an unhelpful error + // from the server. Warn the user in an html string showing a watch icon, + // question mark, and the system date and time. Hopefully this will clue + // in brighter users, or at least give them a clue when they report the + // problem to developers. + // + // Even better is for apps to check the system clock and show some more + // helpful, localized instructions for users; this is really a fallback. + NSString *const html = @"

" + @"⌚ ?
System Clock Incorrect
%@" + @"
"; + NSString *errHTML = [NSString stringWithFormat:html, [NSDate date]]; + + [[self webView] loadHTMLString:errHTML baseURL:nil]; + } + } else { + // request was nil. + [self popView]; + } +} + +- (void)signIn:(GTMOAuth2SignIn *)signIn + finishedWithAuth:(GTMOAuth2Authentication *)auth + error:(NSError *)error { + if (!hasCalledFinished_) { + hasCalledFinished_ = YES; + + if (error == nil) { + if (self.shouldUseKeychain) { + NSString *keychainItemName = self.keychainItemName; + if (auth.canAuthorize) { + // save the auth params in the keychain + CFTypeRef accessibility = self.keychainItemAccessibility; + [[self class] saveParamsToKeychainForName:keychainItemName + accessibility:accessibility + authentication:auth]; + } else { + // remove the auth params from the keychain + [[self class] removeAuthFromKeychainForName:keychainItemName]; + } + } + } + + if (delegate_ && finishedSelector_) { + SEL sel = finishedSelector_; + NSMethodSignature *sig = [delegate_ methodSignatureForSelector:sel]; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig]; + [invocation setSelector:sel]; + [invocation setTarget:delegate_]; + [invocation setArgument:&self atIndex:2]; + [invocation setArgument:&auth atIndex:3]; + [invocation setArgument:&error atIndex:4]; + [invocation invoke]; + } + + [delegate_ autorelease]; + delegate_ = nil; + +#if NS_BLOCKS_AVAILABLE + if (completionBlock_) { + completionBlock_(self, auth, error); + + // release the block here to avoid a retain loop on the controller + [completionBlock_ autorelease]; + completionBlock_ = nil; + } +#endif + } +} + +- (void)moveWebViewFromUnderNavigationBar { + CGRect dontCare; + CGRect webFrame = self.view.bounds; + UINavigationBar *navigationBar = self.navigationController.navigationBar; + CGRectDivide(webFrame, &dontCare, &webFrame, + navigationBar.frame.size.height, CGRectMinYEdge); + [self.webView setFrame:webFrame]; +} + +// isTranslucent is defined in iPhoneOS 3.0 on. +- (BOOL)isNavigationBarTranslucent { + UINavigationBar *navigationBar = [[self navigationController] navigationBar]; + BOOL isTranslucent = + ([navigationBar respondsToSelector:@selector(isTranslucent)] && + [navigationBar isTranslucent]); + return isTranslucent; +} + +#pragma mark - +#pragma mark Protocol implementations + +- (void)viewWillAppear:(BOOL)animated { + if (!isViewShown_) { + isViewShown_ = YES; + if ([self isNavigationBarTranslucent]) { + [self moveWebViewFromUnderNavigationBar]; + } + if (![signIn_ startSigningIn]) { + // Can't start signing in. We must pop our view. + // UIWebview needs time to stabilize. Animations need time to complete. + // We remove ourself from the view stack after that. + [self performSelector:@selector(popView) + withObject:nil + afterDelay:0.5 + inModes:[NSArray arrayWithObject:NSRunLoopCommonModes]]; + } + } + [super viewWillAppear:animated]; +} + +- (void)viewDidAppear:(BOOL)animated { + didViewAppear_ = YES; + [super viewDidAppear:animated]; +} + +- (void)viewWillDisappear:(BOOL)animated { + if (!didDismissSelf_) { + // We won't receive further webview delegate messages, so be sure the + // started loading notification is balanced, if necessary + [self notifyWithName:kGTMOAuth2WebViewStoppedLoading + webView:self.webView + kind:kGTMOAuth2WebViewCancelled]; + + // We are not popping ourselves, so presumably we are being popped by the + // navigation controller; tell the sign-in object to close up shop + // + // this will indirectly call our signIn:finishedWithAuth:error: method + // for us + [signIn_ windowWasClosed]; + +#if NS_BLOCKS_AVAILABLE + self.popViewBlock = 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)viewDidLayoutSubviews { + // We don't call super's version of this method because + // -[UIViewController viewDidLayoutSubviews] is documented as a no-op, that + // didn't exist before iOS 5. + [initialActivityIndicator_ setCenter:[webView_ center]]; +} + +- (BOOL)webView:(UIWebView *)webView + shouldStartLoadWithRequest:(NSURLRequest *)request + navigationType:(UIWebViewNavigationType)navigationType { + + if (!hasDoneFinalRedirect_) { + hasDoneFinalRedirect_ = [signIn_ requestRedirectedToRequest:request]; + if (hasDoneFinalRedirect_) { + // signIn has told the view to close + return NO; + } + } + return YES; +} + +- (void)updateUI { + [backButton_ setEnabled:[[self webView] canGoBack]]; + [forwardButton_ setEnabled:[[self webView] canGoForward]]; +} + +- (void)webViewDidStartLoad:(UIWebView *)webView { + [self notifyWithName:kGTMOAuth2WebViewStartedLoading + webView:webView + kind:nil]; + [self updateUI]; +} + +- (void)webViewDidFinishLoad:(UIWebView *)webView { + [self notifyWithName:kGTMOAuth2WebViewStoppedLoading + webView:webView + kind:kGTMOAuth2WebViewFinished]; + + NSString *title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"]; + if ([title length] > 0) { + [signIn_ titleChanged:title]; + } else { +#if DEBUG + // Verify that Javascript is enabled + NSString *result = [webView stringByEvaluatingJavaScriptFromString:@"1+1"]; + NSAssert([result integerValue] == 2, @"GTMOAuth2: Javascript is required"); +#endif + } + + if (self.request && [self.initialHTMLString length] > 0) { + // The request was pending. + [self setInitialHTMLString:nil]; + [self.webView loadRequest:self.request]; + } else { + [initialActivityIndicator_ setHidden:YES]; + [signIn_ cookiesChanged:[NSHTTPCookieStorage sharedHTTPCookieStorage]]; + + [self updateUI]; + } +} + +- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error { + [self notifyWithName:kGTMOAuth2WebViewStoppedLoading + webView:webView + kind:kGTMOAuth2WebViewFailed]; + + // Tell the sign-in object that a load failed; if it was the authorization + // URL, it will pop the view and return an error to the delegate. + if (didViewAppear_) { + BOOL isUserInterruption = ([error code] == NSURLErrorCancelled + && [[error domain] isEqual:NSURLErrorDomain]); + if (isUserInterruption) { + // Ignore this error: + // Users report that this error occurs when clicking too quickly on the + // accept button, before the page has completely loaded. Ignoring + // this error seems to provide a better experience than does immediately + // cancelling sign-in. + // + // This error also occurs whenever UIWebView is sent the stopLoading + // message, so if we ever send that message intentionally, we need to + // revisit this bypass. + return; + } + + [signIn_ loadFailedWithError:error]; + } else { + // UIWebview needs time to stabilize. Animations need time to complete. + [signIn_ performSelector:@selector(loadFailedWithError:) + withObject:error + afterDelay:0.5 + inModes:[NSArray arrayWithObject:NSRunLoopCommonModes]]; + } +} + +#if __IPHONE_OS_VERSION_MIN_REQUIRED < 60000 +// When running on a device with an OS version < 6, this gets called. +// +// Since it is never called in iOS 6 or greater, if your min deployment +// target is iOS6 or greater, then you don't need to have this method compiled +// into your app. +// +// When running on a device with an OS version 6 or greater, this code is +// not called. - (NSUInteger)supportedInterfaceOrientations; would be called, +// if it existed. Since it is absent, +// Allow the default orientations: All for iPad, all but upside down for iPhone. +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { + BOOL value = YES; + if (!isInsideShouldAutorotateToInterfaceOrientation_) { + isInsideShouldAutorotateToInterfaceOrientation_ = YES; + UIViewController *navigationController = [self navigationController]; + if (navigationController != nil) { + value = [navigationController shouldAutorotateToInterfaceOrientation:interfaceOrientation]; + } else { + value = [super shouldAutorotateToInterfaceOrientation:interfaceOrientation]; + } + isInsideShouldAutorotateToInterfaceOrientation_ = NO; + } + return value; +} +#endif + + +@end + + +#pragma mark Common Code + +@implementation GTMOAuth2Keychain + ++ (GTMOAuth2Keychain *)defaultKeychain { + if (sDefaultKeychain == nil) { + sDefaultKeychain = [[self alloc] init]; + } + return sDefaultKeychain; +} + + +// For unit tests: allow setting a mock object ++ (void)setDefaultKeychain:(GTMOAuth2Keychain *)keychain { + if (sDefaultKeychain != keychain) { + [sDefaultKeychain release]; + sDefaultKeychain = [keychain retain]; + } +} + +- (NSString *)keyForService:(NSString *)service account:(NSString *)account { + return [NSString stringWithFormat:@"com.google.GTMOAuth.%@%@", service, account]; +} + +// The Keychain API isn't available on the iPhone simulator in SDKs before 3.0, +// so, on early simulators we use a fake API, that just writes, unencrypted, to +// NSUserDefaults. +#if TARGET_IPHONE_SIMULATOR && __IPHONE_OS_VERSION_MAX_ALLOWED < 30000 +#pragma mark Simulator + +// Simulator - just simulated, not secure. +- (NSString *)passwordForService:(NSString *)service account:(NSString *)account error:(NSError **)error { + NSString *result = nil; + if (0 < [service length] && 0 < [account length]) { + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + NSString *key = [self keyForService:service account:account]; + result = [defaults stringForKey:key]; + if (result == nil && error != NULL) { + *error = [NSError errorWithDomain:kGTMOAuth2KeychainErrorDomain + code:kGTMOAuth2KeychainErrorNoPassword + userInfo:nil]; + } + } else if (error != NULL) { + *error = [NSError errorWithDomain:kGTMOAuth2KeychainErrorDomain + code:kGTMOAuth2KeychainErrorBadArguments + userInfo:nil]; + } + return result; + +} + + +// Simulator - just simulated, not secure. +- (BOOL)removePasswordForService:(NSString *)service account:(NSString *)account error:(NSError **)error { + BOOL didSucceed = NO; + if (0 < [service length] && 0 < [account length]) { + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + NSString *key = [self keyForService:service account:account]; + [defaults removeObjectForKey:key]; + [defaults synchronize]; + } else if (error != NULL) { + *error = [NSError errorWithDomain:kGTMOAuth2KeychainErrorDomain + code:kGTMOAuth2KeychainErrorBadArguments + userInfo:nil]; + } + return didSucceed; +} + +// Simulator - just simulated, not secure. +- (BOOL)setPassword:(NSString *)password + forService:(NSString *)service + accessibility:(CFTypeRef)accessibility + account:(NSString *)account + error:(NSError **)error { + BOOL didSucceed = NO; + if (0 < [password length] && 0 < [service length] && 0 < [account length]) { + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + NSString *key = [self keyForService:service account:account]; + [defaults setObject:password forKey:key]; + [defaults synchronize]; + didSucceed = YES; + } else if (error != NULL) { + *error = [NSError errorWithDomain:kGTMOAuth2KeychainErrorDomain + code:kGTMOAuth2KeychainErrorBadArguments + userInfo:nil]; + } + return didSucceed; +} + +#else // ! TARGET_IPHONE_SIMULATOR +#pragma mark Device + ++ (NSMutableDictionary *)keychainQueryForService:(NSString *)service account:(NSString *)account { + NSMutableDictionary *query = [NSMutableDictionary dictionaryWithObjectsAndKeys: + (id)kSecClassGenericPassword, (id)kSecClass, + @"OAuth", (id)kSecAttrGeneric, + account, (id)kSecAttrAccount, + service, (id)kSecAttrService, + nil]; + return query; +} + +- (NSMutableDictionary *)keychainQueryForService:(NSString *)service account:(NSString *)account { + return [[self class] keychainQueryForService:service account:account]; +} + + + +// iPhone +- (NSString *)passwordForService:(NSString *)service account:(NSString *)account error:(NSError **)error { + OSStatus status = kGTMOAuth2KeychainErrorBadArguments; + NSString *result = nil; + if (0 < [service length] && 0 < [account length]) { + CFDataRef passwordData = NULL; + NSMutableDictionary *keychainQuery = [self keychainQueryForService:service account:account]; + [keychainQuery setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData]; + [keychainQuery setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit]; + + status = SecItemCopyMatching((CFDictionaryRef)keychainQuery, + (CFTypeRef *)&passwordData); + if (status == noErr && 0 < [(NSData *)passwordData length]) { + result = [[[NSString alloc] initWithData:(NSData *)passwordData + encoding:NSUTF8StringEncoding] autorelease]; + } + if (passwordData != NULL) { + CFRelease(passwordData); + } + } + if (status != noErr && error != NULL) { + *error = [NSError errorWithDomain:kGTMOAuth2KeychainErrorDomain + code:status + userInfo:nil]; + } + return result; +} + + +// iPhone +- (BOOL)removePasswordForService:(NSString *)service account:(NSString *)account error:(NSError **)error { + OSStatus status = kGTMOAuth2KeychainErrorBadArguments; + if (0 < [service length] && 0 < [account length]) { + NSMutableDictionary *keychainQuery = [self keychainQueryForService:service account:account]; + status = SecItemDelete((CFDictionaryRef)keychainQuery); + } + if (status != noErr && error != NULL) { + *error = [NSError errorWithDomain:kGTMOAuth2KeychainErrorDomain + code:status + userInfo:nil]; + } + return status == noErr; +} + +// iPhone +- (BOOL)setPassword:(NSString *)password + forService:(NSString *)service + accessibility:(CFTypeRef)accessibility + account:(NSString *)account + error:(NSError **)error { + OSStatus status = kGTMOAuth2KeychainErrorBadArguments; + if (0 < [service length] && 0 < [account length]) { + [self removePasswordForService:service account:account error:nil]; + if (0 < [password length]) { + NSMutableDictionary *keychainQuery = [self keychainQueryForService:service account:account]; + NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding]; + [keychainQuery setObject:passwordData forKey:(id)kSecValueData]; + + if (accessibility != NULL && &kSecAttrAccessible != NULL) { + [keychainQuery setObject:(id)accessibility + forKey:(id)kSecAttrAccessible]; + } + status = SecItemAdd((CFDictionaryRef)keychainQuery, NULL); + } + } + if (status != noErr && error != NULL) { + *error = [NSError errorWithDomain:kGTMOAuth2KeychainErrorDomain + code:status + userInfo:nil]; + } + return status == noErr; +} + +#endif // ! TARGET_IPHONE_SIMULATOR + +@end + +#endif // TARGET_OS_IPHONE + +#endif // #if GTM_INCLUDE_OAUTH2 || !GDATA_REQUIRE_SERVICE_INCLUDES diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTMOAuth2ViewTouch.xib b/iphone/Maps/GooglePlusSDK/OpenSource/GTMOAuth2ViewTouch.xib new file mode 100644 index 0000000000..4f91fa4ad2 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTMOAuth2ViewTouch.xib @@ -0,0 +1,494 @@ + + + + 1024 + 12C60 + 2840 + 1187.34 + 625.00 + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + 1926 + + + YES + IBProxyObject + IBUIActivityIndicatorView + IBUIBarButtonItem + IBUIButton + IBUINavigationItem + IBUIView + IBUIWebView + + + YES + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + PluginDependencyRecalculationVersion + + + + YES + + IBFilesOwner + IBCocoaTouchFramework + + + IBFirstResponder + IBCocoaTouchFramework + + + OAuth + IBCocoaTouchFramework + + + IBCocoaTouchFramework + 1 + + + + 292 + + YES + + + 292 + {30, 30} + + + + NO + NO + IBCocoaTouchFramework + 0 + 0 + {0, -2} + â—€ + + 3 + MQA + + + 2 + MC41OTYwNzg0NiAwLjY4NjI3NDUzIDAuOTUyOTQxMjQgMC42MDAwMDAwMgA + + + + 3 + MC41AA + + + Helvetica-Bold + Helvetica + 2 + 24 + + + Helvetica-Bold + 24 + 16 + + + + + 292 + {{30, 0}, {30, 30}} + + + + NO + NO + IBCocoaTouchFramework + 0 + 0 + {0, -2} + â–¶ + + + 2 + MC41ODQzMTM3NSAwLjY3NDUwOTgyIDAuOTUyOTQxMjQgMC42MDAwMDAwMgA + + + + + + + + {60, 30} + + + + + 3 + MSAwAA + + NO + NO + + 3 + 3 + + IBCocoaTouchFramework + + + + 274 + + YES + + + 274 + {320, 460} + + + + + 1 + MSAxIDEAA + + YES + YES + IBCocoaTouchFramework + 1 + YES + + + + 301 + {{150, 115}, {20, 20}} + + + + _NS:9 + NO + IBCocoaTouchFramework + NO + YES + 2 + + + {320, 460} + + + + + 3 + MQA + + 2 + + + IBCocoaTouchFramework + + + + + YES + + + rightBarButtonItem + + + + 20 + + + + navButtonsView + + + + 22 + + + + backButton + + + + 25 + + + + forwardButton + + + + 26 + + + + view + + + + 28 + + + + webView + + + + 29 + + + + initialActivityIndicator + + + + 33 + + + + delegate + + + + 9 + + + + rightBarButtonItem + + + + 14 + + + + goBack + + + 7 + + 18 + + + + goForward + + + 7 + + 19 + + + + + YES + + 0 + + YES + + + + + + -1 + + + File's Owner + + + -2 + + + + + 6 + + + YES + + + + + 10 + + + + + 15 + + + YES + + + + + + + 16 + + + + + 17 + + + + + 27 + + + YES + + + + + + + 4 + + + + + 31 + + + + + + + YES + + YES + -1.CustomClassName + -1.IBPluginDependency + -2.CustomClassName + -2.IBPluginDependency + 10.IBPluginDependency + 15.IBPluginDependency + 16.IBPluginDependency + 17.IBPluginDependency + 27.IBPluginDependency + 31.IBPluginDependency + 4.IBPluginDependency + 6.IBPluginDependency + + + YES + GTMOAuth2ViewControllerTouch + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + UIResponder + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + YES + + + + + + YES + + + + + 33 + + + + YES + + GTMOAuth2ViewControllerTouch + UIViewController + + YES + + YES + backButton + forwardButton + initialActivityIndicator + navButtonsView + rightBarButtonItem + webView + + + YES + UIButton + UIButton + UIActivityIndicatorView + UIView + UIBarButtonItem + UIWebView + + + + YES + + YES + backButton + forwardButton + initialActivityIndicator + navButtonsView + rightBarButtonItem + webView + + + YES + + backButton + UIButton + + + forwardButton + UIButton + + + initialActivityIndicator + UIActivityIndicatorView + + + navButtonsView + UIView + + + rightBarButtonItem + UIBarButtonItem + + + webView + UIWebView + + + + + IBProjectSource + ./Classes/GTMOAuth2ViewControllerTouch.h + + + + + 0 + IBCocoaTouchFramework + + com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS + + + + com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS + + + + com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 + + + YES + 3 + 1926 + + diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTMObjC2Runtime.h b/iphone/Maps/GooglePlusSDK/OpenSource/GTMObjC2Runtime.h new file mode 100644 index 0000000000..e4e2ac7272 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTMObjC2Runtime.h @@ -0,0 +1,113 @@ +// +// GTMObjC2Runtime.h +// +// Copyright 2007-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import +#import +#import "GTMDefines.h" + +// These functions exist for code that we want to compile on both the < 10.5 +// sdks and on the >= 10.5 sdks without warnings. It basically reimplements +// certain parts of the objc2 runtime in terms of the objc1 runtime. It is not +// a complete implementation as I've only implemented the routines I know we +// use. Feel free to add more as necessary. +// These functions are not documented because they conform to the documentation +// for the ObjC2 Runtime. + +#if OBJC_API_VERSION >= 2 // Only have optional and req'd keywords in ObjC2. +#define AT_OPTIONAL @optional +#define AT_REQUIRED @required +#else +#define AT_OPTIONAL +#define AT_REQUIRED +#endif + +// The file objc-runtime.h was moved to runtime.h and in Leopard, objc-runtime.h +// was just a wrapper around runtime.h. For the iPhone SDK, this objc-runtime.h +// is removed in the iPhoneOS2.0 SDK. +// +// The |Object| class was removed in the iPhone2.0 SDK too. +#if GTM_IPHONE_SDK +#import +#import +#else +#import +#import +#endif + +#import + +#if GTM_MACOS_SDK && (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) +#import "objc/Protocol.h" + +OBJC_EXPORT Class object_getClass(id obj); +OBJC_EXPORT const char *class_getName(Class cls); +OBJC_EXPORT BOOL class_conformsToProtocol(Class cls, Protocol *protocol); +OBJC_EXPORT BOOL class_respondsToSelector(Class cls, SEL sel); +OBJC_EXPORT Class class_getSuperclass(Class cls); +OBJC_EXPORT Method *class_copyMethodList(Class cls, unsigned int *outCount); +OBJC_EXPORT SEL method_getName(Method m); +OBJC_EXPORT void method_exchangeImplementations(Method m1, Method m2); +OBJC_EXPORT IMP method_getImplementation(Method method); +OBJC_EXPORT IMP method_setImplementation(Method method, IMP imp); +OBJC_EXPORT struct objc_method_description protocol_getMethodDescription(Protocol *p, + SEL aSel, + BOOL isRequiredMethod, + BOOL isInstanceMethod); +OBJC_EXPORT BOOL sel_isEqual(SEL lhs, SEL rhs); + +// If building for 10.4 but using the 10.5 SDK, don't include these. +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 +// atomics +// On Leopard these are GC aware +// Intentionally did not include the non-barrier versions, because I couldn't +// come up with a case personally where you wouldn't want to use the +// barrier versions. +GTM_INLINE bool OSAtomicCompareAndSwapPtrBarrier(void *predicate, + void *replacement, + void * volatile *theValue) { +#if defined(__LP64__) && __LP64__ + return OSAtomicCompareAndSwap64Barrier((int64_t)predicate, + (int64_t)replacement, + (int64_t *)theValue); +#else // defined(__LP64__) && __LP64__ + return OSAtomicCompareAndSwap32Barrier((int32_t)predicate, + (int32_t)replacement, + (int32_t *)theValue); +#endif // defined(__LP64__) && __LP64__ +} + +#endif // MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 +#endif // GTM_MACOS_SDK && (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) + +#if GTM_MACOS_SDK && (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5) + +GTM_INLINE BOOL objc_atomicCompareAndSwapGlobalBarrier(id predicate, + id replacement, + volatile id *objectLocation) { + return OSAtomicCompareAndSwapPtrBarrier(predicate, + replacement, + (void * volatile *)objectLocation); +} +GTM_INLINE BOOL objc_atomicCompareAndSwapInstanceVariableBarrier(id predicate, + id replacement, + volatile id *objectLocation) { + return OSAtomicCompareAndSwapPtrBarrier(predicate, + replacement, + (void * volatile *)objectLocation); +} +#endif // GTM_MACOS_SDK && (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5) diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/GTMObjC2Runtime.m b/iphone/Maps/GooglePlusSDK/OpenSource/GTMObjC2Runtime.m new file mode 100644 index 0000000000..f284542ca8 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/GTMObjC2Runtime.m @@ -0,0 +1,163 @@ +// +// GTMObjC2Runtime.m +// +// Copyright 2007-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import "GTMObjC2Runtime.h" + +#if GTM_MACOS_SDK && (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) +#import +#import + +Class object_getClass(id obj) { + if (!obj) return NULL; + return obj->isa; +} + +const char *class_getName(Class cls) { + if (!cls) return "nil"; + return cls->name; +} + +BOOL class_conformsToProtocol(Class cls, Protocol *protocol) { + // We intentionally don't check cls as it crashes on Leopard so we want + // to crash on Tiger as well. + // I logged + // Radar 5572978 class_conformsToProtocol crashes when arg1 is passed as nil + // because it seems odd that this API won't accept nil for cls considering + // all the other apis will accept nil args. + // If this does get fixed, remember to enable the unit tests. + if (!protocol) return NO; + + struct objc_protocol_list *protos; + for (protos = cls->protocols; protos != NULL; protos = protos->next) { + for (long i = 0; i < protos->count; i++) { + if ([protos->list[i] conformsTo:protocol]) { + return YES; + } + } + } + return NO; +} + +Class class_getSuperclass(Class cls) { + if (!cls) return NULL; + return cls->super_class; +} + +BOOL class_respondsToSelector(Class cls, SEL sel) { + return class_getInstanceMethod(cls, sel) != nil; +} + +Method *class_copyMethodList(Class cls, unsigned int *outCount) { + if (!cls) return NULL; + + unsigned int count = 0; + void *iterator = NULL; + struct objc_method_list *mlist; + Method *methods = NULL; + if (outCount) *outCount = 0; + + while ( (mlist = class_nextMethodList(cls, &iterator)) ) { + if (mlist->method_count == 0) continue; + methods = (Method *)realloc(methods, + sizeof(Method) * (count + mlist->method_count + 1)); + if (!methods) { + //Memory alloc failed, so what can we do? + return NULL; // COV_NF_LINE + } + for (int i = 0; i < mlist->method_count; i++) { + methods[i + count] = &mlist->method_list[i]; + } + count += mlist->method_count; + } + + // List must be NULL terminated + if (methods) { + methods[count] = NULL; + } + if (outCount) *outCount = count; + return methods; +} + +SEL method_getName(Method method) { + if (!method) return NULL; + return method->method_name; +} + +IMP method_getImplementation(Method method) { + if (!method) return NULL; + return method->method_imp; +} + +IMP method_setImplementation(Method method, IMP imp) { + // We intentionally don't test method for nil. + // Leopard fails here, so should we. + // I logged this as Radar: + // 5572981 method_setImplementation crashes if you pass nil for the + // method arg (arg 1) + // because it seems odd that this API won't accept nil for method considering + // all the other apis will accept nil args. + // If this does get fixed, remember to enable the unit tests. + // This method works differently on SnowLeopard than + // on Leopard. If you pass in a nil for IMP on SnowLeopard + // it doesn't change anything. On Leopard it will. Since + // attempting to change a sel to nil is probably an error + // we follow the SnowLeopard way of doing things. + IMP oldImp = NULL; + if (imp) { + oldImp = method->method_imp; + method->method_imp = imp; + } + return oldImp; +} + +void method_exchangeImplementations(Method m1, Method m2) { + if (m1 == m2) return; + if (!m1 || !m2) return; + IMP imp2 = method_getImplementation(m2); + IMP imp1 = method_setImplementation(m1, imp2); + method_setImplementation(m2, imp1); +} + +struct objc_method_description protocol_getMethodDescription(Protocol *p, + SEL aSel, + BOOL isRequiredMethod, + BOOL isInstanceMethod) { + struct objc_method_description *descPtr = NULL; + // No such thing as required in ObjC1. + if (isInstanceMethod) { + descPtr = [p descriptionForInstanceMethod:aSel]; + } else { + descPtr = [p descriptionForClassMethod:aSel]; + } + + struct objc_method_description desc; + if (descPtr) { + desc = *descPtr; + } else { + bzero(&desc, sizeof(desc)); + } + return desc; +} + +BOOL sel_isEqual(SEL lhs, SEL rhs) { + // Apple (informally) promises this will work in the future: + // http://twitter.com/#!/gparker/status/2400099786 + return (lhs == rhs) ? YES : NO; +} + +#endif // GTM_MACOS_SDK && (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/OpenInChromeController.h b/iphone/Maps/GooglePlusSDK/OpenSource/OpenInChromeController.h new file mode 100644 index 0000000000..35363a710d --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/OpenInChromeController.h @@ -0,0 +1,54 @@ +// Copyright 2012, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#import + +// This class is used to check if Google Chrome is installed in the system and +// to open a URL in Google Chrome either with or without a callback URL. +@interface OpenInChromeController : NSObject + +// Returns a shared instance of the OpenInChromeController. ++ (OpenInChromeController *)sharedInstance; + +// Returns YES if Google Chrome is installed in the user's system. +- (BOOL)isChromeInstalled; + +// Opens a URL in Google Chrome. +- (BOOL)openInChrome:(NSURL *)url; + +// Open a URL in Google Chrome providing a |callbackURL| to return to the app. +// URLs from the same app will be opened in the same tab unless |createNewTab| +// is set to YES. +// |callbackURL| can be nil. +// The return value of this method is YES if the URL is successfully opened. +- (BOOL)openInChrome:(NSURL *)url + withCallbackURL:(NSURL *)callbackURL + createNewTab:(BOOL)createNewTab; + +@end diff --git a/iphone/Maps/GooglePlusSDK/OpenSource/OpenInChromeController.m b/iphone/Maps/GooglePlusSDK/OpenSource/OpenInChromeController.m new file mode 100644 index 0000000000..30b18b5e21 --- /dev/null +++ b/iphone/Maps/GooglePlusSDK/OpenSource/OpenInChromeController.m @@ -0,0 +1,135 @@ +// Copyright 2012, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#import + +#import "OpenInChromeController.h" + +static NSString * const kGoogleChromeHTTPScheme = @"googlechrome:"; +static NSString * const kGoogleChromeHTTPSScheme = @"googlechromes:"; +static NSString * const kGoogleChromeCallbackScheme = + @"googlechrome-x-callback:"; + +static NSString * encodeByAddingPercentEscapes(NSString *input) { + NSString *encodedValue = + (NSString *)CFURLCreateStringByAddingPercentEscapes( + kCFAllocatorDefault, + (CFStringRef)input, + NULL, + (CFStringRef)@"!*'();:@&=+$,/?%#[]", + kCFStringEncodingUTF8); + return [encodedValue autorelease]; +} + +@implementation OpenInChromeController + ++ (OpenInChromeController *)sharedInstance { + static OpenInChromeController *sharedInstance; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedInstance = [[self alloc] init]; + }); + return sharedInstance; +} + +- (BOOL)isChromeInstalled { + NSURL *simpleURL = [NSURL URLWithString:kGoogleChromeHTTPScheme]; + NSURL *callbackURL = [NSURL URLWithString:kGoogleChromeCallbackScheme]; + return [[UIApplication sharedApplication] canOpenURL:simpleURL] || + [[UIApplication sharedApplication] canOpenURL:callbackURL]; +} + +- (BOOL)openInChrome:(NSURL *)url { + return [self openInChrome:url withCallbackURL:nil createNewTab:NO]; +} + +- (BOOL)openInChrome:(NSURL *)url + withCallbackURL:(NSURL *)callbackURL + createNewTab:(BOOL)createNewTab { + NSURL *chromeSimpleURL = [NSURL URLWithString:kGoogleChromeHTTPScheme]; + NSURL *chromeCallbackURL = [NSURL URLWithString:kGoogleChromeCallbackScheme]; + if ([[UIApplication sharedApplication] canOpenURL:chromeCallbackURL]) { + NSString *appName = + [[NSBundle mainBundle] + objectForInfoDictionaryKey:@"CFBundleDisplayName"]; + + NSString *scheme = [url.scheme lowercaseString]; + + // Proceed only if scheme is http or https. + if ([scheme isEqualToString:@"http"] || + [scheme isEqualToString:@"https"]) { + + NSMutableString *chromeURLString = [NSMutableString string]; + [chromeURLString appendFormat: + @"%@//x-callback-url/open/?x-source=%@&url=%@", + kGoogleChromeCallbackScheme, + encodeByAddingPercentEscapes(appName), + encodeByAddingPercentEscapes([url absoluteString])]; + if (callbackURL) { + [chromeURLString appendFormat:@"&x-success=%@", + encodeByAddingPercentEscapes([callbackURL absoluteString])]; + } + if (createNewTab) { + [chromeURLString appendString:@"&create-new-tab"]; + } + + NSURL *chromeURL = [NSURL URLWithString:chromeURLString]; + + // Open the URL with Google Chrome. + return [[UIApplication sharedApplication] openURL:chromeURL]; + } + } else if ([[UIApplication sharedApplication] canOpenURL:chromeSimpleURL]) { + NSString *scheme = [url.scheme lowercaseString]; + + // Replace the URL Scheme with the Chrome equivalent. + NSString *chromeScheme = nil; + if ([scheme isEqualToString:@"http"]) { + chromeScheme = kGoogleChromeHTTPScheme; + } else if ([scheme isEqualToString:@"https"]) { + chromeScheme = kGoogleChromeHTTPSScheme; + } + + // Proceed only if a valid Google Chrome URI Scheme is available. + if (chromeScheme) { + NSString *absoluteString = [url absoluteString]; + NSRange rangeForScheme = [absoluteString rangeOfString:@":"]; + NSString *urlNoScheme = + [absoluteString substringFromIndex:rangeForScheme.location + 1]; + NSString *chromeURLString = + [chromeScheme stringByAppendingString:urlNoScheme]; + NSURL *chromeURL = [NSURL URLWithString:chromeURLString]; + + // Open the URL with Google Chrome. + return [[UIApplication sharedApplication] openURL:chromeURL]; + } + } + return NO; +} + +@end diff --git a/iphone/Maps/GooglePlusSDK/libGooglePlus.a b/iphone/Maps/GooglePlusSDK/libGooglePlus.a new file mode 100644 index 0000000000000000000000000000000000000000..0f66be83ff1925838eae3ffd94348c0f682a9f26 GIT binary patch literal 668920 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%0DM`+xFM}_hh}- z?YCcSS$og(qt-v$Hgmui`K2rRS6*?-pl9dqTmI>JU8_HN!|ZPw{NcqKAJ49K_`xbS z7xu}oHDyliQ^xQ8cm9B07cHDS>d6ncKYe2Tr{^xdb>UgpHkdNwo}Tv|+0&;a`_+;M z_6!+&*%w!heCW!*H@x_xSD)W?|5bOldbZ!~CmK$9_w(*|FPoZOYh%IPJDLp{kTvCt zqQ~F3uHmacez|1x&<}1ZT=0)e-mcMp{L8KXeCqy|qYq8H?7>$WcFo%|^OuPOA3gir zhMTG-dQAA@%DbyQ)aZ+)V>kV^?c(mEPuV`S)yzA-dZcCE;fsowzqt0fH)~Gc_|6s0 z+CJHS`RCc|uUXf!%H03zcGJ@PUY#*=_0We#KYjF!XYT&xXIJh%{ief@-~QH+3v+(@ zyMc>dp1k~lK5wsYQ8YZe%jJDLe0%KJTXP5PIQ+|jL;sjt|E1SgoqfZH8>ioP`nplq zJUGAPkp-`9KT6}SLgTK3G)CGY-H`!E9x@YIh%86eAzP5$$RXr7QjHr@^^m4WJ0uGk zgybV7$V_A*vK(25Y(aJ-hmhk)H4dtJNK>R8l7$RH@{tl`CbAG&j;uqrAiI%6$Z@0^ z9Zfx?DbfzfLIxrENC`3%S%@r0)*)Mv-N+&2I8u%Cem$fq(hkW&1|j)K2{IE|h%86e zAzP5$$RXr7QjHFw9?}$Phh!mxkbI;BnTae!mLnUGZO9%(3w_j)XmL*l(h}*2Xd%x~ zWE?UTnTsqzHX%Ea1ISUN3LRD*q%qPK>5dFQ@{ozhbYwoV6j_68LUtnWKH~ehM<$a# ze}8_|~99R+_`hJU3Fguh!1pRLY=zb~hG{sQA~ddM$yS~Y=w z=PrIvW^95ydVeCZV~Vz*kFppaPxbhQSw2P&V(G8g{A<}>e-9XLbFatk>9o~vzxaU1 z^KBpMPWJd`#;vsUM$jNi|;A8P3@pXB3pw(@Fb z<$Hzg-x90u%grCk=hwD=Y%F@GqJ9=3lq%zufsw~FI^{udEP?JKm$zu5Mn zeZ6A&sb%f`7TZ40w*Sib0e?>{#j`^;@u=;8DBtglAKGj6xt{+wi#Ofk53u9meDhbd z_S)L^=VIIcM>uCFy~{%V89$WAR0hNHhvVz|;1By_IJAddRvw{!SGD>N$NN7Ghx`=U z_MwldVf9;N`CVZC2durGV)Yg3uaU(I?Wfqvdw{j)zZsrl`Td2JXXu}PX6c3g=LYkC zY5m17E&VGEuQnWxhf6Jf$ln(8hyLn_)kg)(|JV2_(!@6$|Dg?CYR8;x`fEvntIXG1 zzkHXKY3QeC8$TT57e49v;h3lt{1&f?`9mLah3#YLV>_Eal-b?3{iD_gJYsDi?87kl z+K1PPA2_#K%+QWTS+suZ%e9+>(YUzbzVVu=fIF>%Nc!6JJ z{Lq(NWNk6@4abZh+R8w~p|2Zm`44UT25Y+^f4{K&g}(O;Yg?hO57#*=dr39Fo35>d z)kGTq*L;1#<;CCr>QGz!eUZ_R`MDhRQ93SFZ=tP?rERr=qqe1VRiA2C>c4c(&~d4D zs{TpkqJBW_KzUM|S3j$BkJ^s*qZ`V5_?)oykC zsZUcsu5+)BAC;TRUgfDaq4uh}R~u6us87^!toqaWS?37#3#t#De?uP@%8lZ8f9d6> zG7FquFcu=UJT-9&T4j3*SB^}Kry-{!Rgp6hrhwda?AhpZkvd3yBm=nw zX^dQfG(%b-t&txg*C5J={;uVBd!z%>$?RRwH`wp)=q&r)8~qcc5AsuF0HPQ-qX*gV z!RTQ~4w8$ELPjI`$XG<(j1G0GIzbh?Ye%Xu$`4hQxYzJxG*ytGm=hc>37Tkv!!R)u znTPAjzWMs1`#w11>le3NQ*Yz;b9OD6G41|)GMe1`+@kr{efCU#?^<7PU3brH9RuHu zn|JT&%P;MD@Z0|`&hB-|9b4vKGyb%nZ8`IA>s!D6IkvN(no@G(A5J`(w|jc`L*SYn z|9t)z=yNlld+tkg)(buAUVh>9GycKvbMBhg;>k{1?qB3$-#2JEdHNYmTb(!O zl9q2T`}-Yt{UYO*>%M#8y8UgR`t**oo_c4%Rlm9W!|%J*dFa4D{&dFTr=P7kv%#3J zG8Zq+ZkG4q2k#$a(yn>O-9H`l&Nt^j-r%FRZXR2D=5^2ZZF*u9yCZ{A;Z z`0|=9+EtqM_+CJUkca3g-^v;pZoM%pWk1% zOYJYeU)}Ys-%MZ7{Efcdv!Cp~^pvSN_n+VS)x~WupL6R!+q9l`Xy>(8KKQ#KwX5y? zaLTMBZGQLa_kVn%-KskezWPeyv0KhM0YTYlNaJ6lb>IdRL!%X@YD!`!|5FF5^)z4v_D=-``!mbKb>ht@;=MfsuDJH66~@&cI=1bCoQJla@$QBH9Jp!12fNSd{mQE)SAYMjci!r7 z|5XjLI&!kki&bWN8d?U0+09ArE)9eD~_f#|w(FY*m?7M)-N z5k+eCCFpQGGra{9!`Y(jB=4DM0Q=<|4mBUPrbge?uzNppQnHBi)f( zknzZ4$YSJ0n|kt6 zP7;vcLmBz5EKmNjT||DA3e@(0r?J@fS5W3Mb)de?bi?`Hw1Rw?sb)uSdbm|&I)`Mm zVQDgZA6obdv@6^2sk%OUozHXGyA@^Gd$F7MEdP=7yn`~&^B4HY&vFj5Fy~z7$$3{g z@>3`y7o6hBza%e4e)%?U>X#jvKJ4zdzwjEjeS4H`f46P_PICKKn)vOnYY=U}oSoFp zeng9~R1UIdG8H9v=`WZ2R4QLAR>fv0OXa>BJh^5v;eR#s$AOS@u@w80Vnf*cbs~T^R7Yg7Mq5j~hz4V( z3Y^Zw(xGHoeTgBeO#LuftnJn+ZcL`Q-LiUutQu?ZKwluoIp8Re^Lj)fU$azxlMLBC z$EUJ~c4K+TBm+wLyJSh8X~jOt)g(SncpPYAwY@kB|5b)J?Y=OIwSq%Rtbfs;SoozA zeD>N8b=kWeWqbU-HIA_`X)mi$=DFYjALbXgM`2#>;KOXW(uKLKRYV>@;VaEUh^1K{ zEK_FEbO%isV&X@>l(JBkSREsC%m&l<%{;j>MNE-ihBERbszQ;+5z9<3+x8quA^p{j z{q_qp-1e`cjQpOFmnO;Y-R#Lbe9pc~=4|y<-m}MN^KSoF1jw+`6y>Q~`Ou zDw$@!<#`l&CWQVzqxonzG{s~&J3?YIRlYO|+05GCYTA#LYy~?}%%JYf^etX(*q-oa z`qNZznwB4#rgLTy>oX{p;v*YzrIJ=_V-!#Dzbl; zaA!M+cDcOpA6HH^T>aRT;kQTG_H##gPu-t5&$TG?486sB8uW0UE-3RXvO{WGvPkB3 z_2ehoC>s^OMH%_K>um>r6lJikmEVSB`MpH$R15?7kCM>e7gnTKxZQ9jEhrGH5_@Vp zdu^6&cx`0T?Q`wyINu2Lgox!O>oHH(jRG_Ev%)&X?Ta=`95K^vm@F^46jj21_;HlV zMPxvx(z7GeIRm_D3a!_&H-f!a(5u|uR~(44g#Th~A%`n>BiEx274lZLFjg~I+Ws{t zH0bRp^L)j?P2}Iu;5-<+M(g{(-VAePnxf4k;RWm6;B)vu3Ji6;y$m zVf)6(3cC9~pIDty5qbM%w*MK9{5Z-Id)`{s#ScZ~8z+16QU(V^egkFXG2}>9*D;xm zMHG@uKj!!gIkSP@Eo@~bO^k{49qrdj=`yfPeGp?EyWE?)TpXoX-BSD@$vU3{Ms?Ad zIwgCzqAYt;D*5dnN*3I!c1R4O0FBkj+TWS9KQonH;Ww;tNtBmi);(=uL(AS37V87T zQ``D$W}}J`q-?CuGJLFl$@8kCrvFFZ?dE-0!KBNzXpT|5ycpJRj)%yr{+Bt^%f=|RTx&JbTyRw5TV)zJHF_b_3RbsChqeOTpxIMcY{EJTa^H2e|rbzN) z+b=wWYr7cU0#S2rfnSX=QH>a`2acCVQ*iT`e-K!uAGV(irVU2;0C;K)=MzlxYJq%=Yx$UeDfr76o=@hQ#PFNo1~Gg9OkWqp*P^^8vHfjIU%?R+`R9YbjrnKOjb_FC zyTSP}+?^3~VGOSW&xzsQwBhU+UIN}3i(f+K;_(iEJ_8BA|-@YxlS8V&~;7KuDi9ROIuLq9r zf73L7*EE0MG@P5pF9A1lFV0n0zi1mS1?g z`4xXBnEi_U!kZ;bBW{4FwR`>Hy^ZY^c*2S!yvlgY53SX@zZvjWU|S4NSUlnR$RCL} z9iG+^{p9Q8} zM1JA+h|*~dPiq~I!OO(f9G>G7v7-;iM7k;R7k8Ly~Eh{H%KfK7EgGu@pi!5_aJK!hbKyRV|yQ-uz12- z@y};&cZJ&zi>DFs?S4g z-y7T+f2d#KR>*qNYht<%`c-f>(_dfg^Rxfr#M*_d9bG{=9mKZh;>0pxr6arpzv2$Q zBT@PZ_30EMF@GJNak^e@-*$93Kzp(tmD-p$6 z3a?~pg~a9@juUK);R%Z;JQu(EffdAA0G^FM^b5i>5yhDT?>O-b;Z4JKFFfH2#1Woo zyaISfu}_9qjBPYLVey1V8gDSXeDb08mWyowJYn&K`x>t&yd!MCn0URgb%Q4?o^VIw zwSqT@?duaS6I)Yw!r}=x#=nhvSV;Z01UJMlEWdDFMEh9--T~roCB53%s=*T$Pxw%V z=k3Yx^}LFB`?2lHNY=CP4*ag33#n)DHvCaNgEu2e?*(`|hcTN{`V#tic*6WWC%hQ{ zZqjc;oE6}O_=S}p;dx5uLG~**QMw2`Tj_x17oLg#_$w6>xeF4dbHP*b3(GG&8Bsop z;l0Ur-9{uzCt({0Pgp$ReEf$=x0rlP1dqfoEWhwT{5l`($3Ga{AAdMM2zN$w{A8ML zjMni}&-78o0m{dri+sEOmN;KyW5y+F*TQ>^w-cU@hl8~1Pq4iYFB~7jTk)%Yzoi~` zfH&h0^)37+qV!&Yr}}MJlqlVR?In1jzJ(Vf%J%}()6vRziRm1);$#!2J8=$??*Z7d zh$F1>5zaJTLwFk~*Eu5-rA@HagC{JWa2@=+$@fddX#lQ?Us!(O>iDbtp68*SqJ4s^ z;1`x(xFVwb9Ic;NTZ{B-v0uj-lO3s_SSBo<@GeBW)$lY1+eiD`fNdo_Vg8;IUXH&p z+vO8y4fuKd!ipoj2!96oFJymS058BVEWhwP{AzCl7bZ%d0nfo7+Mn=DMERHkuOV?+ zXHhy0+r99F6-RiY@e1ITutr|}MlrTLc*5cd=PEyJw~{#d;GxQo#StEeD826Rwvf+B zx6!X)>k3a;afCbK-vpkBKMULrzp(tmZ4kw&0Z(Jzw<*8+=&JC9Rer*i@b4r2_lpyy z)xjq&f|yywxcqUWKt-AuOI!*)H#CramFo5ps+ zN?&+_@d}JL8eSo`(J`KIH~hM`XhJzI9>%u6Ua~(H-fO(=@OH94$Kmb7wgsNB(i7ffymj!lW3NuS z8?mi{CoG=uD&sAMcbIzbI4V)P9NS`e!r}?f!@rH~S`lXfcou$P`Gsd7%Kv0|Td;S9 zHx*k6JYn&KCm1ge-XY>oBE14^x$uO=6Xqp|Yd5hS>nlt1!Mq%CnXvrA-SB5oKgV=@ zfji?DmS4CXqWrXm*ADv$c$wH*!V?xxxB>pA=5GS7k6&1R;W~)o)Pz?Ld&NBZMQqjK z35zFu;sVZZ^rP#jk4oU97jS;FepUDgeq9&Txs(3#0u+4=hES~Ue{CdW3`ou)(eDF;C!tx7GN0i=Vc!!9eLweJ&mB14gPk0>urfj#G zIK|-6_=V*c9*HQ;dcCQd;FL)b%VflsEBT8>2yr#4d-pf?F8rxEM!r}=p!CwzN8vhFLGx&w&7hZsW zI&t@s&SLOf{KE1J&qkEaRCs&HUpD!jfo(E8Vey1Z@RzXNVd6{&PrxrMzi=U3xDWGgnJ^4y~eacE4{|> zRUSv#|CZP?;D_=Nu7_Xc@iqQN;M({@`3TpfLD19E= zGw_6!j_?BG&4Ra^^tzJXTx>Jo35zE@4SzA)eL{M3z*F!G%P(ArD7`#*%dxj3y?kuB z@Px$^9&Eg9csf7jlimPqz2JrOm2h|CwTHJ4-a>d?v1P&&Ry^U>#%ls^A?26Hao-YK zLwLgC31{F}`5YntjluQshw>7xjVS+B;ORUykNj81Rta7>ZwWJr?CND5`KbmzeqOR( zgpZ!*;~YBApSPaBo$Fm}`{6CL`WN1dU+1aXzhFKIyc>TwZwc>2l%H+zX0qKX;_Se- z1)i|>OL!xG^@9a>(@wza@P~d;cnzXBE8(dh+(?|4uq}rttT@7p@z=39&x04@7nWam zKB73Y;N`R3cH+#%#+rs@!r}=}Hr_aRIzQE*94BHM4KJLpgh%4n`DqpTE&%7?59cf4 z!HCk!hBt`#CrED~wqEdrm7Z`n{OZ5^6Q?J*Gyc$@3uofj`RgG5_TV=7!}(0OC8Bg1 z!c)KfN&(mJ*fQXSeq6X7{zaryOq|BxI{1Z^58*0^;w0cL#6A~ZC2YrPQ}4zT{+9bh z;_a{P+w~FBJA`d7ynWWLg+IYRpY2{C&Oz`l{K864csrsvZ^K(oJKsq^vklvNc*5cd zzk*-KRf2iTH^DFA567ACD*O$JJD7Aff>+=d*7m{+5T!F4p8DI_)Wdvi)8U2wS9q%O zfxj;O<}C2N$_H3+geT#jOWf)uiP9DlAczM{{z-x}JAv|I6gzFlwI=rsf zo5HJwttvcW@q{ZG@7TGCwYk`H;GH;^`aU?7hWaob>JPq_6a;;@q~Bb z-$^-qNjdBXZ^thzzwkChao&VClkL_MM=!SLCHKpO#S>m-ye05dUz4eyrPvn23-u>F zAHV9WGX2_O@ErW1{)A`YpHAE_NoO8-I(}iLBRmyRekQ_GeQAFGUTnqiLj4Jk!>{@} zPMi|(X#Ao6ghwKZGZ@}F^3#|62p{BFow`0HdvY-92b%QUg{0Vo)uk-x-q}vnR4!^Mc!fg?y+Y;V5>}^T6HMXYk zgvArCjbG>0I`?rM3a*JioM(lrA&PV29L8Ie>r&FIgze}#jJNE1NB9VSjpMdZPRCih zau|OY*9jj&6lV`S-S_NEdi${Lf*0-cBp3ZJRy7lzxw+<+%Gr+-iJT* z|H8WvqV4fg|=1+x} z;?KaJhkrGAF@9lfFFXrTKBmB1NPDS4zcn3O2|Qu(geT%3O1itr$7FCJeqs5AM z`_&QL9KW#q!u1iwsS2+i?XL>QYYl9b;0cQ-oWS3g?Ya}E3i#;RiDkm_3m-Y#$2oX* zV(nJmYrd25)7RK|4riILc*39HS3j_a{2l=B!XNqt;T`yU6L%!(><4edFRbl_w;;;z z26)?uQ5Q!{yih*Et?{cos<7W3 z!7cHJ@)2%=C{BHNx}H&eG{9B|Ubwyyu7O|Yhx${Pw+2_kAI=xT$5_mx`!Zjf-h+M> zyxsH)w9;8rgZsebXRhi2+kzV02L`Krh3_@qV0gA>u5>iEf$)U+drr7N{_bqImpD1# zZ2ZECBit4L2Iemdb)N>@9>1{s!kPGW-{qwjC^v9x{NX-~a1%uNtqE^7+f5)&ZERKH z2`i3pMf~bNYCXX50zPq8vOf_%c9!q2GAXyp;3N1$e3rA*UVUt};Dz&} za8=_St4_NnAFbI>Jq&)NI_=u>C43ma#s#gXGH=R)!bAAOI6+v?Ln^%;@Kk<%iL)Eq z`|v_}3U4*uoA5L)SW3P&VcP&Nj1z?QoTcKef~Wj0gtrFU3V0#^!b^-dAD-?HH6Yz* zu+4!N?iUG9!>@7H3*>(mcryMl?h-D>uW{Df_$PtK;Sb|3;gN`|XL#D5ZKN|4TYq?A z|Ae!R$Frlpp5gVv)>-LUc?!2TUMqO6o^?M3TXT3(J%gLzclAu1HsD71qk0B6KwLe; zbM*|bKDJu$LOBUnHQuo^t)AJ>1hyk*#_Ab-7{89=h7VHDXHw7j!?-|r52EyV_R5t* zf8y-Iwhdk=2jMNo+Xzqf`U3fS8{2w#p`L|bG2Tje%I`DqUc$BSGOYim~Ox6V~w~oQq%m#U|q9 zfd}Ic{fBU0L~(k-+roHg0{1Div2}+htT@6Q@i!&zKGNw5Ziio3e&N=Z&Qa273vM1u zN4O!P^NF5g)_J57TIn9E#`uLeb=gkOza6Q@_{HK1A4bGG0Bew}y5tzeu9=9%v3JS02;Q9knFH6-pz(iw*>51z2%28uGWD{O!TE9iFh_32((emv*p6b8^5sp!W;3ce=K;2 z`6uvt{Gq=Teg(gdt33Q$z^n0x<4kxdejP7U@h=DS{To%D7hLOui~% z<0F^Lgq4o)5&Y^;){~ElVBV1z^)JHvs`~tHgja>_GF3j<*1;229N|^QTMDld_P#0y zY)jw?izmFuc(dUpu#bc{58F(5!r}=}$FKd%U_N&)cq;y|zrqvotA2QISLwarLj0k= zgbR>&+5cR4>Q@i3e|gvj!wdbaaDV)&w;jY82+qbI`aR)p_!HdUShbSzB)BtvVdYb} zDWY^5z*|H*6WE_d*y_R)7Eidg@v6c*j{N|<8rUks6BbXnqVc{xopq|%kHXVC4-cQt zI#uHdAH=Wn*g*Ll0q?^f%18JU{3?&$4|5#`=DihB`3P@El<&>(N{IUc>1@UJHaubF zM|h+0R>4!bErhoQ+j4lJ9EF$SSGi3ly_Mi4_(M4g&qkEqG_(T<--r*|D4-hAZINxI9-I>dTRWHH^@DBy&;XeZ2i(goNVZCot`F76Tc;Dz-P!n2Gw1>Qb*1K>@^b}u|(#S@-ryj*y1Qjh(aj~tC{5IkY=ga_c?!*-L1 zlLOAiFD$=sFZ}ASKcU?FgL~o+{kL#S{Kpx`Y@mN_3vPm6SlbIX#J`U8W{`e!aDDv3 z@(b6+zmxP9k$!7%ZT!OW3)jSdl=J=jtGLbv^M0CT!tx7OK~!G}c!Nkcn|xHpcI-6j z-SQ!P_%zSsJvVNi^EkXi*!I8+^PIxGuhsSIyUEW!@J{^d=aruD7DVZ7v^eT#^}W1x zu{gqOEKY)Q+W_We6;T{vy?aM-=EJK-KJ&=WB5ZTv2`fF}>G(HLUd@@eoCThWUs!(O z$@q7Zzbw+32`<4eEWhwXMCptZkGQ$yw;0=K@hlzTq4-rV*`zZPJOF>FC*i*MRWB1r zXE3-I{!mZCJ@Knv7XOa%Ex0TGP*1{{i1N`Ap2~A3aav<*3NMtea3kZ@gSQ>tN_ZLA z>cA7$ehF7IUL|-X?1%1uSH*UsGX0C~m+;ZbKHV=XGp~cauljv#2jB@Sp71{7?Shw& zeLB27*tWwH7EgGa@!o{j4!hFbjBPzUVey1t!M~0CR(*ti0sInvVflquA}WvP;cdZQ zAKnYt7Q+)3Pgw6G7H&(%y!#R- zA3PL)D1YHWh|S^Uf+wu>ggYBA6W(s(&nLZ(*jmFA7Eidb@iO48!#)RI zBW!iy35zFO+w!%T^y-6a#_}ax9Z|k2S-w`otAZ^N%a^bo#utxwKCeAa{K}lazQ(ry zRQe@LPgw7T7H>PeL)dj+dKb2>@Pw5w;q}H_4R0O$pP0#c9NP+b!r}?PV7w)!7rA#9&iN{(NIcNuRRyb`uw0&hFEE%1aDPk5v8 z*1(&MeH*-W*jB?67EgGE@fO01b3yp z`+|Go7nWbRBmR!0bA)udgWKU3mS4CPqI8rL<){GptMUqF9O*qeZ1L4giy~dRLP;7nS39B50d*j!Ak!{2o0M5c6?vn^- zB3nqWIlMf!Yek$^*qXo-Rvh6B{56QPpE!-c_3#VJFI)#vzN^F2JX_V-9B40k+xj zLiq~MG~N_=jp025ZyL4|c*5Eb;fcl@4R1Q-UW;>uVZ5Ui80T>Q>dF1l69hR@fpLzV&x8-)pGcf(#Q74u z7r(I56aECh=9S-MepRnM*@ZvMGYh|uzaIJPOn!HOx8fJ(=X1hu<5&CXiGMSA1OCvS zgx4Y3pVjb+Np}tTSc7dPJYjx5C%oKvOW=*e{wBPo*q(tWES~TJ`3L`27FKD=D)hvALGmIqH*JmJCkbzClG|8l_t z@rUD7xF@3h>uhoA(vG@eYafdvoN2t~@RpO$V)ES@TN8M~{CrNh5&qt!uQ;v14e$%g zFI)%z?$@}#wVw6^u8Ch*e&M4D<^joXjXBKYgTGEN4`}rze1N8-{oV~v{n1p?*@JB- zywE=hZ^d7Y?OGFOJ9smGVWlJdHu4&A)>(SJ;cdXSCYGM?BJ=0tUjkkj^9#>Mh%sgs zTKSp+U**4xd`-i4FZ@s+!o`-aS;U(JE{NqzI0sRE^o2KwI46iR5L<6}!pgsJ7XG2& zO4REBa5wzI@(XuH6eknj0PMBlwa3;5p0Iert&G7SZzV7k8PT@QNx4%5Fh z{haB1(|M+^GTp-T=TrUme>44(=~bo+Opi8wjp?gR>m@MC@9_t`e#i71re~R+VY-Xy z4yKRNAhi7v)B8;CG0lBd=ig%bH>Q`Fo^E=o>9MA7H$BpHj_D?*8<{@UbOqDL@Av5) zHT_%Dzc&4p>7SW?$n*oIN0=UFI?Hr-(_Kt=FnyZoQ%wJJvQPgXrvJL9xu&l*{Ug(-nLfqzt`Z+_hv`+OSD1d#^!=tgnZC|+Ez>nj|K)BU z??cn$Oy`@QPVw5cQbvC>9b7loM79V-e7v2X`Xj<`>8i-s@zAK&N1D*5reu3@^G>4U}Izu)wyraw0Qn(5!0{<-NVO;<5p$@KCfA8)DYJ4}x; z-OF@O)0ddO$n?Jp{r2CPe&6&~(+f<`Gd!eV)`M|=bAp- z^tP0vg)Rsj#;zK!fRpXYTy(>&MV>^#rm^i`%?nCAHmXXm*Lr_VHf zy6I!JyC=MtR%a?_WZu5Vh;A1Hrkm_E()(R021i0RKve`@-D(_2lyX8QN0 zmz&nwf5rc~=_gH3Gd;z0k?FCfhnm(}du@NCX?@#MIMcM&&MGIrgxg&Zu%|LubY0w^h>6HYx>uw7n+`LTHh*FJ|8tb&GZ!0 zMW)A^&NH2Bx}WKvnAX~I#bZ6X(;ZA-Ynt`t&feJc#ilPbUEOq5)8C)t`TsI~!1O-T zADjM*>CL9!Hmw(eDnGwBz1;Lt)6bcH+Vm{bGfdxY`cBj1Olz&S;@)cdX49zWm3h-tlNUpUKjchj9rUuT;2 z$d1><^d+V*GOe}8ic`(>zt8sgJJVWIEdQ6L|I748rnj4B9kJuRZu(WzFPUCt`q!p^ zWqOh6g{B`h{jljNrtdX9*7WVB2bmsV`X{DuG~M2Grs=Cpw=kVyx}NECO`mPLqUjSg z{QmvJ^yjAcnbx{nmD68Le`tEE>CL9!F#Ve8m8O@Qe%kcUP0uhr&Gdby?>3!pI?wdY zru&)hWxA*7E~Yz}ZfUxy=?hJtZ<=+uZojIVKF#zgrjM{zQ~Q0`^xsW?X8IG;yG*}p z`W@37Os_NjqUlo8i%mac`e&w}FwOc|m;Q9qcbOhqv>|0o0-1c zbOX~`yQ(;6nLfkx@#@}w)bv-Tzc9VuwAQO?`wvX(n;ybidn(QPRHt7wU21x4~OCnI2)9^`efib)wp?i|G!gTbgca`a;v^o7Or`ZCBa!zgQ0_{7=*S zOlzH{?0+)-N7Jmobat(~l>gVJe`R`+>4m1BH2t{g8K$S1zR&dCrddbn;%beg;tw;e zZqBh!sdYu%&x=bAp-bXC)pP5+CD74iOQ`jF`Zrav|PvFWX*H=BOL z^lPT|ol?bHZTgp{pEEtj^eod4nSQ`@v1zSERGeX^Z#CV^bWhXQn*Nb#tqIh2tQ&Ot zT+?Tpu4wwi8Q%X7)1RB(XIg9g6z5N-|7iMc(;H3y-t^0+SDMxuJ#GI>)6bb+V0xbE z$4ozBdaCKkrtdI4#`H+jIi~xV*0+C@UPsgIOkZvKO4ChDH!^*m>2pk1GhM}W1=Ihw z>ya-_A2j_})Bk09o9Qj4-!lEWX|3y1K3AB2*7Q@RS-0o>(@o!Hdc5h8rgKc+V*011 zyPNK6`a09sm~Lgdx#`PHUt+qRX?=@H`8?b7nWkA^=j>WnC;vZLV=R-i7yGc-2gTke_HMBRb%RxJIV z9K}0F>};|0TXUoriOmx`Tx?&ly~HMqrFVDexGrMj#0JFDJ3F-B6#HWvhfjz-CiW|_ zpNl;pmfqu`(4m0u$8{Yv@lCS|7)?Ii~YCQy<+kH49D|(vG0ie zmDpd3{e{@a#5RdtBlb42H;cVqEZ%Q%u3v1qSiH}|`gE~V#L~|&(RCGw9VK>{*ui2m z#b$`@DYl1LdN+mQ!TT$0qs2yw#rrC(<2@C&-+LYVo!GC%eku0fV*e?2pV+-(|0wqN zV&4+`8?mp5eM#&_vFpXI75jkLd&S-*cA41g#MX+f7F#K{Of23*;p@6o>{zj*#SRyn zD>h4PU$NcAhQvmSjSy>yJ?)Wv6Z?(WFU5W)_Mc+^CU&pbJz{?^_8qbEgFU`q3O_6M zDX~pr*NDAM?9F1Y7rR(&jo53%;yn(|pIKsOh`mH?f!I-Ehlw35))t#8wv*UcvF*jS z6?+nuk?P~OV(Hxo;zMHpCHC)P>D>tY^L;4x4`P2Omfm^5Ki^idPl$b7EWPJ|f4&FB z-XRtrwPt;}SbC2E|9sbqoi7&eEwGOF71&M`J63F-*x_RPitQyfB(}5I2(ezV$FZId z*LPIx7h*pZ`xmhviv5Gw--+EOcB|Ou#6B%{gV;yK-Y52Mv3NYk=f`6uw)4bRioHtg z7Mf8ng*Na^zwn^+7v5jI^iCrnSb$->0UMsd*Y?aswv9rWZ6I(2{Q0zFd`C{|L zYQE+Q4-lIvHbbl}HdSnr*aWdbu>rAuu|Badod4s{p2eU(=E1&+*D9!8B+eAJg_DGX z!ZE^lua3i0yGX~M!hKK2AB818Bz!=4pD?w5w11cI4&iOWTZA_WuNS8FkK%6>UMbul zyhONGxJr1I@O0r~;c>!w!nwkk!nSada8Ni#*e`4dpEBk8Vfp%n4+!rQ-XpwAc!w~x zZ*+Zl&x?=WB)nd@Nw`sXrEr7r5@C7=>rLcumGCU#>B7ara7ZQ&&0pm2<^ zU)T^ng?h#15BnySzwiO!eZqT$cM0zh-X^?7c$4sY;U?im;g!M-!b^l}g{y>T2~QU; z79J;@C!8ysDQpWT2?vE^g#E%We7!q_x5FY!eA~pX7k!;@lh_8)mkTcuJ4^JL!qdg( zi9TF7S8S5#3Bp0Kls+u+)3A>%3|nf4xWZ$~Ou}?GlBf^ien;EX4=I@lpC-1ylMq z!REtmh0TL~36|3LYgkGj`n~|A?|E2C-!EY)ecNCuedr?tsj$C-O@e&|meThOETwM? zET!)SSW4fsu#}L^u#~-p>Xjimd7Dvw+Pewn8cgJ;=N4f^&4cJtrlA)mfp3b@Uz6u6gyq)G_l2E3&oBTn=dv` zERB08zFe^b#Ab@k5NnG~6`LeBL2OWLyx17A0kM9uKCy<_)5w21|0%J@#U2&gEcTGt zgJKVe-7j{Z*u7%+h}|uAm)Muo!BO^Ys5B+ zT_tv<*cDJ3wrv z*bK3@*i^AeViUv$#m0+`5gQQe7wZ!X!}&_=z7G@HIPD3o2XMP07N4)7uOapw#{r1j zcT{Y%*h69uiaj89zu0|ZX}y4sr}YA|yT$GjyHo5AvD?Msb2l7*tJp1KH;dgQcB9z! zV%LeKbp|?L18fL(IV|7zu*f@Kwdhr%SBOsQ3KV{t=*6Pvi=HR?aM3eG&k)@fJwf!K z=<%ZaMfZtrh<+U7KRWMGSW0IzET!{+==(*d^#juBhbt-kF45_;E2M7|eXHnOM5lEF z3cp@-e7=RlH;TSW^p&D77k!E7^`ck7qABrJ$o^Tff2QcuMV}^mvFL@Oj}tv#^gPjr zi=Hd`0MRo=&k)@fJyrB1(Gx@uiXJa|jOYQ;{i6FsH$>OC%dcNp+ zq7N56SM&j*XNsO7x-ELD=t-g{h#nL@Ui28z1ETvy_la(Zej0^L<#kH>$>Xq8?ngy$ z7X6Uu2Sq<1`hL;(iN06#J)-XxeV6DvMc*O%cG0(qzE$)sqHh*`ljs{oUoZMP(VIkH zBYLCgt3+QZ`U=q-L|-oY64C2LuNA#o^eWLSM4u)4Owp%{K27vu(F;W%CwjiO=8!;QurmX6s{hY!qvhCVJl!Mo>{Q8e#Pw-2MM?pRu9oNrc4bMRu&k=aIlG zUEB;_;^IT#DicqMp`i&ubmxVQnl#l_3P>s`DA-00$ZaD$6$!PPFV2G4YH6}ZsF z72x46o(0Zu@l0@ni>HGFE}jNH{fn0CEe1EcxDdSG#pA%cU7Qcz?&3V~W)}|!uXAxO zc$JF>fS0>C6I|`$4Dd`B+u%YMr-FyOI0>BL;skJli-X{Ri{rtkH?&-T47l0F0q}ko z`@y?i>;rFiu>s!f;?rJSzl%?SSGo8&c)5#@f~#HJ44&!YL*POe9|R9~@d0p#i}!;Q zT)Yn)aPeO7X?pk|_xB!fvx|3w_q%u(c(;ppg15VP2Y9oKw}aQYcpG??i?@Q8yLbz@ z+Qpl}GhMt1Ti<4di@c?|4%W3H^H^F9t54afb zO>8*$Yv+8lsFn-FyyUu{W0-s9NtnTzo~EgSyiVgtOx#i!dC8=iFWDe!6+9|tdW@lo&` z7vuX~HWa$}5O|1-4}#NNjQ2%01YC^wMmBuEttI{Yz@NEzFZfR`-UEKs#k;{zx_B3O zwTpLxm%4ZdxYWhl!G$i~2F`ZzR&db8Tfl8xyczt}D=q2Y1m5Rjd=JfrcU-(4{H%-D zfty_11isD1Yru6bZUmRQcon$N#Vf%>T)YCD=;8)&z{ShK-~Xy5{Y$`~xwszuCl}X( zceuD3{G5xcz^h$c0d8>dEbtr`&jc5`cse-S#nZrvE-nTKTwDk~_Hs-5$ASOp;(YLK z7w3Urb@6cUlP=B$uXgbOaD$68!Sh|50lwVDHh8RyQ^5mVoCFTJH~}2t;vo3&)|Tsu z2mi^%G2k684uGF@u^+tJ#XfL@iw*D`7oWy>sKmvmz(ZVo9GvFjqu@9fH-o=_sU^LK zz@NGJAb7Wn4}gE^;{D*qT)Ypw%Ef!ZOI^GNJjcbm!6h!<1*xOgSlHHQTs#f@^QNxVr{WOv3{|qWF7X9*nMJmiQOi4lh`J) zE5$AmTP1e7*l}WW#oA(nV*O%IA(JVe4~g9;c9&Qh65$^|mnPdJyi)8Eu~lNHiybF6 zSF9~IDAq6b6ea|4eTT&E6T3_7HnE$;Hi=y+c8S<3vD3wl6Pqj678?}%6zmTCe;fbl z9l2fje-qx3jxzo5@D73aRq+2U{ucQ7K1>dC6#h8zV{ic#4+ikGY$%SA5)u#`of*PdDOldOCI%{Bh#1Z)X?{;t#;*MII1;9X?a@9QpW+Dt(9h zJEHH#;^#Nw2k`kU`b-7InE;=@519N6`1BbK@@K-ID1HO{O7RcDzd`(Je9rCXs>9!^ zIzI1rK>SVce;^;}q2DBFhqRJUzjKmGKF&qInS$@KW`8IAYs5bU{|@o#MU!X5&%p7& z6~77o=i)cUqX2OI6z6{UJ;fh~MdXR%Z->7?{9=5rf3^4<;nU}<=-B0`5Wg4y5d2TX zACAT3w#Y{cvkSf@{z@!1(`Uq3hmY^KXTKT#_2N%QJ$PCC)9??IkF*~^KKPNRbZmp= z{B{BP>)_89eKY(!#orEpJNY=)xXm;^68%tje9te+fb=BjL&!%wn-UG`aCOz zUkU#Q@qHeONc<@5uPr;|}DIkC4TGP0Ke4OiH z#91l+R`@mIzYKq=_?O>k8n=pnJN$dZ-va+3@qY=QzGs2r{0;nP#s2{Q%i{kDK7CgM zh50l5-;2Kw{-4DE8+`h{2MY5q_@9gaIs9+L{}TRx#6J$d4cZ_IZ{B1Y(c(wI?<#&G z{1ox~!tX2oK=^~j9|nJ<_@m)Z6n{K?`u+=wzX*P*_*cQ7M?Ug%ILhKK@r&UY=!^wfAL{Zk z#t(=8r^~1B@6~uJU3!Dd@8I$+SNMBe{tGU@0cqAace#$;>hdwi4~O~M z$6q>fYH4ce*y7@8HP=)wD5$J2ugb5iuAEy|U)is!Y(YhJWnF3bz&h+HTUdF{u_g27 zE*QU{Uv=4nxr@r?R^otGkxPKG`ufWGwe=+>MHdXR5Q(c?c){Ie<@NJw7L?T2&08?{ z!hsgmS6w=<^19JA3+n4?s+~(Xe~S2Y|66o?&i^4zI9=s}`g!GLl(q||s=Rt0LXFSA zV1R2XmzGzR&08?JZ2pC_utEw4v2b};E?6|bvX0L@X;D2}6j$lO`Z6l$s%uK;l+CLS z7a$I)EUzyuTeuJ<)>^HYx3IS6!lgTJ;pjyR>ucsu{BM%Eu&QQJbp=+$HqktHP;x(Os@lg3ON`QL=oFq2D)IIEGz zoGz;NOH0ek%Byfzx3svvY<9J##C?{Q7`WzY#JE$Po9X-sJ8PdFGr6R&ro1eCmKJ$r zU0s>B1gegmHmPVNYIOG5chIz&Mdej;P7OM_B!3>?8)f*RD(9AO*)^uFuBNV)8jkw{ z$-HE0VXGr1mpBD?jy!T9DiqysMkme*C*SC&v)!94?H zSIdF6ybc}7HB{v)uDP_bZXpgP)*8Y^oL_^xdV$gBiu3j%NwX{G&RfvZw_G-_zKUws zQ0ZHSUTO5bz>)J7%&Vv1Qo#R1$coCcYIJNDj;dl_MFFB)SaqI+%%Lu?vTkT4)mNN^ zFT|;zNeipW>MAS7a|=>l`9JRK$6+<7b5RltU5KPjl(Q2oVpZ8f%9-(0KHLwSeN27b z(vo@A=s_q@OLud2pt6>ING|7WICiSks9r-C)-0+kuXNScp=Ape)Hq#-X0oR9|B!F) zlPyH=QnaY`MbATLyI=wOpDU@J&E}yb6>*~5Z@t4E>msxbdkr)?@Sy*0HTUp^QiB_jZ`JFFNtE?`os3<90e0D^wf|O%WTz5{O zR%heecL4@MbZZqYI6J*)Waic3#<*na__KE`tfUqi4RrlESGa&ieU+0duPeDwm>Oyg zvT@6w9cM)i_wR)@XqeC5Q(IQIkeY~={BCunG?J=7r%)&Z{n0fQtiGX5-v8lxsvlW zxH$uI{(m|{P7b#V=eEc>IW+M+f1{j}!>2#bK?ArT&)bsc49Gq!kIz@W|Cc&5h`W-3 zS?)~!FXKE2a6pdHcJC$X~--M znx$d0G;p>C&ep)$8aP`6qfv4af+kQL4V_0P^tYWd5` z8I+ZqH6V9zR#MWyOl*>}Gih&DX6B#)1Co+*v$F>e;^4E|ha(!bsJ^~tLBATrr+@GL zaQG)YzNAO3ycUb)XoG%N*GO_WkNore1>^CX*VsoEkGJ`MvVkW%$`a7Z{tAF z1`1DqH0+qYXm05|j0YUfzii~x0^Ua(%8!K=E%}jv7c%0Vy_CiLr*V|co?TbDnBqJ; zjw9FLcZ(4E?L~S-*q$2)Rz2rdVmYc+9KT!O9P{%f;bYY~Cyx1L<$O8+U*f2qx1f?v zaK1R`yCpRp+mRpg+~J&bVEWE!Y#qnj*BM3$o=ZG*vvUbM5l5Zlo)bsyqS^IJX}P~u zeso@j-&jN0e7V9o(6v1$4(fa9uXP+7ZZV7#$j`;ATt_?ZIdMp_oD)aWfAG!*j;U_r z>l6pw1g+x; zCj$Er#~gPgt<$j%aqK|2+O~K`4m$u}wuMKJXZ^{_bcJ3}>o{iqh~HMl5t;ZRE{fwg z;=rpXXZ=YW3+a(lUF94q+q3dx1>*4Gi0KkXe!p-XquZYUBn~VXR4u^d9@lnuTeuQ& zlpz5#=?lSJe`n{1JP@d+RGeEb&1dlYYdCgCJLg&okq*2sl&s zcThi&+un)8m6Wh6*HTVT9?q8^wTL4Lq4On<;r;PGjB|ABbZ{J$A+7VH5phu93dG_5 zJ5PRa<4$2(m&bs;h|RPfaJ>4GA!6vmQsGAdQg6x z)i)#{jsv)iqY}qvq$3I8@E6Y3(o!c5$`#ttI*!v`Ja>n_r-PFpo3q08c*H!+T;CM(8soj+sNkbv+zqFYKp6S||Hx2enh1 zz=L2jhojxXO-f_0V*IB++E4!>8+bj8KJ$FRzm16JoSa1HEiZdsSh1z(#DXb5R_!=u zn4ZR^`P-}7RTZ7sRSPzj7Of-@)oaU`k0 zw`I%gjW3%o+=94Lr%+4=Vxo9fYFNZmh0wp1II30WIJP4W&z44x%BgIS^lizu2p$SaaG9$MkA$Z&5@!Lp|j3# z*AWx(H@sYQqT(S%*pH{j~s5_zU_e>M~V&qrUv}1M&9@Dx1Z1)`|Q@Q-)#TQ*D*h2 zeL%+zcOB=$ah-8o#9hvDK02;}jzdXgW^??WHGK+}q%*hj9Pi&5P zZR^)N?xHhv`k?25q7#$PIr6>YbC3M%*F`5fN{mKT(#*V9i%vW!;hdVX>+8Ul*PA_e zzkbLF9DQB#qr-FGIDa8vY%}Oq5i+D)0SN zn|?VJ=k;8l-?_?69(%v%!J-puxV9CYXe?Pu^~BquYFYj*mxK`3E+OvrK+%coFI`6a z+7_SktjAUNxRGmA{*273rTIp(RWY9mcpYJaN3sU7c^dv^u|jZ?&jQ#aDX0#LQMIB*L*8`nl+ZLVZ97nM% z&A&eX#(e)9?F*LWdosHI%>PDCfnf(%8%LkHx#)yv+&-IjpJeTBB;^sDwAHrZXoS z4i9ZAI#HcP*RwP~s;c-@&%B}&vm}-vLf&(zqY<&T=tNr&U47S|U!Q;GWW{0sT2DbH zYR0U?w-$t&JhpN4{f6#M-PRUNNBhS|2l%Wg{li7aCR}UwbIxGgUv#Xq(|R3Cy_Ph} zK`O@%Ym1I;<@RhD&M@HcEocMxCn1Cpwbn>plE3rVtd7*Sq#bTs@au1k=(c;hJe$&Y(1GstU2kmpI`7NZ5clLGQI>J!@kb)|bb7eQ zrWrUxbMqsIlYeM)z&;ZE=8vaV;!Gez{xqN6v`-D-41 zu03+te%Po&N_(FmgBK~0G@q^D7o#-7y>Fd6ZT2AU=W;c79yle3#at;2fv1?b3 zGT-R%U1NPJTCh~KU{8G$^m*QRq5v&jhoi5f2WY0B>}|OC^UprJ2)+4R_&=(qLI)xk zsNgS#ei=9X_YZic$3x>VpU+EUp;)+{r+t0kw!x5-#|384gPEq$#^)d1{TPEU6E9yH?f%95A0Pbo^vw@uH-3jG>5XHY1`tA$8bGgxu8>h-$gfiEc3SHoA#?bce+@aGa^MkN-M9 zjuU$f(=0?pW|!w2qT{=~(BcPI;4qJe0~uXk#16lwLx=)XNKX{qCm{;Y80|?x^Wp7@ z7%8YXw)3kno`7jmS;|QQX3I&$-{XX0{Vy$k@OCCx>gn6{lv%22c2 zX$FeB9lhq;wiCN;T3mb?gs8E1Bc*LS({V;udPg~?ZHEvYPlvR1a*5(l^qC=wN6~MF zC@w{OIhJ=KqN0d=(VY`vJZ(*rf7_u8=T%!L4>_+KKS)>Q@pB*};Z-^^BEjknYgm)9 zHLZmdjYTgj!MxT3vBOgpqI)ADC$2lkzyQ0$Z%u{hLI8ytSIe=SZ~P#nxja zOvLon*Dl&R#k<;rOe@4&eD?(f_<{nokF_p^HLUA+*IL?Ty@4%~dL`~^U;8ABesjgJ z(x^GGMq-O&>1Sko?fY1Zc^Cg!jP-)E>o|HAU;AO!C%o$vwx&g&a`RY&vGrQ?%Q!x( z4BIx=5^URAtFeu+=ofnY*6*-wXZ;;pTwNU=DEJ~fSYBAe>VmCl^~Kg>jl$MzU4gC7 zNcel18!)=${BF%s^iz)}6J3|PbBAJDRD;UimU$;{`CvL5AKJFqpa z1J16~v@1H=>IQ3A!?883%dz!X^f5B8)r76j`Zcy~tdB`C5=uHiiixu5x7ZAef1%A9 z3Lhs*LW}5&iML95R~@#dwF=vVhP4^yQ^TSkp8L$O4#RwISV4U3>yTlMfce6(=D>W3 z>xB8ru;?fEnhon)n8WyS;h#~|u~AlkSi>5Lt!dG(1$wNjvGrQZu|=kLi-Bp=#d-{A zSZ`u$TJ)obNZjvfSH~nP7uK}s(_S8{fi!C)wz#k;Jf!i(MOz1ehIIm46!t?T#s{rL zdV*t(#MZRtVT%a6b);S0t(SS%N4$$Z7=>Mr@UB#A2&`evz}B>uVT)ZQXs>n0vam%mBJe=O*D228-(I!oQ$-%@bvnrU7q<8e+lLgObG+3H*0e@r>#^v= zI6i9`wtkC#>^9nZ3EP{Dgxfk{N0*+~mq2_B3uIb@vBe?S;&c-_Q-BQXDWG9}zyZGJ z0Lj>c0H--XjRux`cHv>w6MV{N59)*rBKXgD2Veaa3xo`eUwU`H^kG!=mLI2WXlOWPSiK#u;U;S=yqk15IR~XAH%N1Nf3Qjpd~%Yw<40>Xb`MEBd`xEl(jHzP484_%i(q(kWzp$y^Y-a%+v=c zP)sEK4r1ay_`ZZ|W0LslIUklokIXH_PAdUB6DPEs$%tHwou1LqBYY_@!Y?Mb(M;R` zo0=5a5;Qa%3jJhbQU|`2D}a<|Nz@~ILzTK0;}&2okcuZMhOt2d$1>io*pYD+kTNML zj`1PIcqfx~;-rZ?A^Li_(qlwULp$ev4_zJg&=CpVBXFrNj_BF`3K~O3-a-LZVp9(E z`4g{)@ux()uIUyibmkPC3biP*iH^YcG#T7wM|(cy1HJD;ZYDRociZn&~g8($5#ujua#D9Y`_0)Rrqs)&ZF>e=1IECR0w_gCzRKEMTTQpdTdl z6|7{Y#Jxd0_6g=+W7`LH%QtQZ^Sjtm<@ZhbfO$W*^h2k&twR?w>c6ICM3()Is^u=n6tN0{L|5jJE;5r@j68`XaAJ2bCC+1R*)5z`hwbO9dSp z3K)qwbef*7+t7&o72;|?61o}r9$fEUY>Ei`rg>5`(kU5Ku&F2_r>I}SPFDJ9QNONA ze=X{FRmt>4P_;AD|Ex-u&!k(xO#eicY@bQ@h?#y=l^mZ*H<6j{>mlbF;4|qiLv;v2 z%1`YclSDu9OOhRn!zg!BFS0`k>Z9?&Et&%SJ;J5-U>pLZ8D45Kqc&@)DU2o9OSzev z%6PeAPsVwQRH)w2^+3w*R1~V|4c!6cG}w&U$h9?;q+X0aN*Sc~W?YTH)U2fTVSEyJ zkj^`h(1=`0qlv3<57H>uAK!i%uGoyEU&Quai_NXTbbl{jo8EL+_qmw|42cgzjknvk zRG^UF_jUjY_JlSdzzT%5scAzq&AC5XOsS8I_>CtHkN=>_+~Gm|M^`qA(7u}={j zk-r6`dHv(VHN!ulbi#0F8S$zVYt2YgWs1~{ z^!}<$^_kQ_qpn;Gsn|cvRadC;&cYS=X9jGY82Yaa*yC|=R8jm_+4gk!RCxZUr*7cpv`^Iy!Ube>CGB?4>&A&*ysc6_DB${}@KCbN&KGt#kgdj9TaX;~0t%|Kh*d1r3rX{VH&r8+ROutRT^!AxlkInSes-RzqWaV*H(66*- zoYOZf^vr{LwqKNVo)AeWvI_Y zzYFC#Rfh2`2jw@a4Cj6q%6qDe;C>g%epN2=VOf^qIjYLVJ|9XD$xJ*0sX8q_CW!`G zlxUj@Y%kS|(E&RhKHVN^`2jlZyvxJ zky9~eN*o0-KW#}%M-m==q}^xRgVAohPbqH<+C`cn_ajO0ssBq`ZQHZpbKyS_uxsIS zmTt7|Th)KUwja_k&j;+6;d7iX1?}IdzqO@ZO}n0p@~_y%r&z}Lm4;l-NTtJjZ(!8c zFYQLgwkRyl|C<=2fs=5tX$=k{uefAy=F$n*idz_a=-yiyvm|5FRxplHyp8d4#oHOL zQM`lk1|a9mO2&H?@ARa2yrJiSdnr|S5gL&n!;kEbO!d;Q^wP3cMy!{9;FlJ#GLpQ1 zflCWk8GXFpz@-JMjA32x?~PWyZEWZUSh_FT^4Kc0(uK6I?}7XOTXiTz>Bh*mQI3{u( zW|&>u^+R^_Zy)*|8qMtbbDt6NKxRazLfNqiVLL+W(;>2cHilw%!ABJTgq-@G<9|i| zw~l`jzV`{7kyh9;W4s^2rS-GS{)vbB!(?J4AHI!g?VfTev!iX(!aXh9XT}BX&)`#& zkr^MfN8%{t|wwzZ1v)1DTQ8&9}VE>5tsYYh@3)pu8sLlLr65A zz1n9wBb3P+X1Hk!v-e)=^ov&0`q$8alRavYfole=3!s~RTlcEY<7;{ECGf;AT6-#xbQ=nn4*OAT) zRhIcY&ba11J!-j(YmAKf2oy8}y^9Jt%Isg$S&23EDy%)mV@wJ~t7Gt8hztbzOB zBh@rS2%tC0R{#EB@)-CW&+19=yf)$O1W0DQi-4DM$>1JX?%=*##G zkaMIT<4-_bzY(ByftHa=Ad@i~?H6Ba7GpQXY{vdTzK|U5Sn`0Jo&k&#bT3aZL(_Hd zAl_T9dj~Vt0e?YxGK6uJ;!wt)138hy7@t-g&OO+#6fqM*hxV2tWer6Unwd~ zeSnt&sXYx~>I0moIDv7g;zY)`Ff{m#j-AA~9(x%lGj0RY3@%WFZo?Zo1pJ9`3ZoAV zBI8uX&Wf00A#jS~G{#II_4a{F7>5F5&{hU6WjqSxe7=nFM_>Z&o#9Do;|=vgtH|Yh z1>+FKnT(TwHU++l@g^YGx><~?fSem;j1L3(M6(&61oow4F^s|!$H-6ca%P$*qj$Pa z%`*DQZ4OuJ~nY^G091=B7XQMXLrGBE9Omi+uVaulXr&XQjMHxI{y zX_wQWCutZ=yPO8Szbcq^ISu+4RWR*x8uW5KJEmRElykW%n07g9PBZl=OuL*lr)pI& z?c!;vk+=xb@{F#o9$cq68N*uIpN!!FTie5o5q5Y4oN|Z45|v=(Ct9F1v99wIE@)K+8LNT0oPzJ z4aYMucLM6%DI)jqJG~C%>q_Ak=p7(mS}G$olXPcf^km$N zy^L72!@TPsKw3b_NM~#Y@`-H5)k%^nXVLBy_BPW6=2DYmME(sk4DVqK^yq1*H_GeB zc$=PnMn`*x!KJ5~F;U)2;nGvgnE1p;2O(&T-7=F(o&lu;Cebndu7YPK&&4)K+`oo- z8MZW0i^;l;IJ8A5jGh7S5gg zlPXxSaPH);xspOGSU7hwK3?gh84DK9ojgSqELb>q@{Ous!NR$d*QA-IyBS!KYEl7&0g1-N9)k+ZB_DhX!Z|P#^})OkSYZ_H2YSS zu`)C>68%H)CRMcMsuA>JkO!p~UGgbBlNV9wMv1;85FWEfUlx^2w-#08=*y3AIJ#K6 zi=(lQNtek!)-mY<*}p0f!e?H2p(L7XLTI=m-DPG-mkXomE;B=g*t>`lg1Zbz>n5BI z+*ZIRvG*R@i~9(;OA+@G@NbHAADN+(igX{Dp;*)by4j;?@!JepK#mGyA|S2Ta0Xxy z1Wd!;pV5iXSL1EE#JZtq>9VjuJuuy=>~;e3kY;XK$pIUmigx^zKxhJddTA$%Tk%k- zB3F*kLLl8)SzLQUw*V_m6XwxOl zY?G4yXM~~5X_M;nInB^#s`S)m=$I;4S9Y4AB306T9&U7u#M6*c+qiGDMkI$`DQna3 zA$Vr;2Ic-QG3yh-Hkr;7!Cg?PlWCLv5xgGBUt)VPI=nVHS%^E<9*r&Y04#&S&!>F@ z1IZQe??O?u8N86g-Ht7lOq(GA`+oRzYql902t5g(vZIaD0lom_4D&fIhlPclC z-D9eR2X|Uq1`hQ3_|ahc(;5a{6h8n;e_0iDQG9Qt?@$F@lr!%>pbEMu=Ly3Pston{ z{M5ju$K}bDqKk@PC0iAAQO>i1VpY&ZIdkgys$As5g8+(Wxhm+Qxa&@DR0UlW>H^l? zH>rXy3Z(;On=0s{P&!clpbEMu+$m80r3$(zG&NAZR|Q=Z?i5Q15(jC~*HL$%C z^_uGIz==V-4XV$5wQr!)c3!6F zw4HN-RM7`IZRa8&bz=khP7SS8L+ZD;7$inwC5tnUCHrsN_^ zfPVpU%*Zp~mq2>EWFRsXcoN9hi5m=f26!Fq#SI3GM%AYFejtr7&5#Y`i^T}jV}^zU zY5p`2T_JEPkovEI7-0ghRz%MTTmasmSP=A}G(dW@QJKlGK^?rv6F#O%aC@~h~7sED(Z-hyX_-dA?I z+D{eF?rO80uJ)Afb*hU8Y2~<_>JqQ$RF_oX=eW{rr@9mYsrSrwx?1X&-$K%}&+clo zov!v8Tmxlpw$s(#2&CR5Tf15#@;w}z*4~?(zJGwT&x_lF2S@40RKYV%r%miTO2VM; zaoWVus-W+2`ll;YN!R|VS`|Fgbo!^8Pz00?FWO&cnEaS3Xn*-WNFS_cNBipxlap0J z`|AvoZ_=32{yM|tVHywGUuT${rV83$XPCTJk3#$F43oF2g7%loFa2#*(Ed6t=^j$If(R6+aeJgd!91?{i%tahR*Xn&m*%*$25 z6v1hKy_y^0_P0{QgxlW+Rl@D>J*r@e;N$jpohoR5y{ufVF{AzUuyUs=Xn(oY?z>%; zQ9jQZ`QxBZX>_BtseWD+G{a6){kAG-hMlJRFREas;560$Rs}N!ZmQFNQe~VzpzWM5 znLAz|&}OPKfh!G~{K=ZoiCk%*Ow=%iTxp&p;G*D)%GMOt4lxtKe;z|Q$u`0z} zX`tMz$`r0NP&TSEl`9RDt*Vr8rGfIcD$}^qK>1jeOSsZN`9hUTxza#6smf(sX`m#H zk=&Thl?KXCRc3IdfpV!Tmvg5LrB;mr*0_eI#Ii37-}hy^jX$6Yy!V#rs&xqM3Jnz^0p-#unbkqwJ9S z8v-^pd942;U{iz0J{I7qR$^Jl(YHZwlB?!sqio9kA)vrI$Fo z&jjp0z=vyipA95a2f*Qf{V6 zIQInX3iUt0Lk9K#6tJlS;P4*>lJ8dk61ID z*G=Al?N%($dJjH>t_=eqY&oyFFAA@1IjghbQJR-~v(SeKOar8KZt5WXsIzy*Z8Z&w(FI{5#zce27OwQ1$;}kg; zdL|w^rT0(;Jrie4dbTR)nQ&d`7fVz@&xFPb%3M{@GdZKE>r_F{3+;CK`o3*oDAa|>~FbI@H7zf=ETpZVV0g7>spe110m^- z$xXo{DD_hVb~Aj6FgGn=n>dQ@-`w;7udh;5mg~$A9}Z)9J>q{T5zqvWfag3;FRskAxSS z+*Ns9FXj>0QXdxo*mz?5RtCepKp0jF#d(8WNHC2Da=cI;F&hv$sBHLz@~eK{g(qawF|>2+$#Y)0Y0Y% zuRBod!1}8J`(o999ki#xr`A7yd%&)QPvsr|dcdYOh~s=CDw*0f_TOrRXNI1~HV5@E zei%2euL7w6 zLnYYD`8bjB8pT4!Rf>}s9|v-(CNpjW(n?f(5##R_iy1#woWl5p;#3SSyrI*IC5$m> z&N&&=7}FImVZ2!JQpU@HTqu_@mI0?wwV2MhPWR4W+^TpvS|)GkLm;hR#bdYxq_<}2 zxo|wjN5Iu6c}9$nfKLNCRkt$!MiJv9?ER}EjgRoch$4-TyrKUnVtj;GB-&Hk(*q~M z_y}kLKf-MqkMR-kV&Hd#7#{&I1#&)Pd<2{eAl-%3ULr zS5)~-*ZMwI<#S!@`;RJzc-oDllCO|+eW7c87pwB6uJu)^@|CXj-K$EouJ!#wmBZRI zZBylI?U~+J<%sr7hg3PLYkl6ClAdpLt*@IZ$8@c4v?|}~THicXj_X?AFI4$X2a!8f zIiYKPe^KS6uJwJP$|+szGq05Ne6MSL-BtNP*ZS}yA6y~g{-e)Yu2$u=K5My2l^^w4 z%cH9Nq-%X|s&YmKto;tCVwf^w?dQEpPG&;lA#1;6l{}Dm%-V0HN?u4jXze#wC7jL~ zwf0-7QX5D-Z0)yErM8fG+}dxKN)eDS4)K}&{-Kf|5=JVJPN>ul62>x+B1!bk_wMwMb9b!6#n zm0}^`c15TURq6;ShNUAa#X*W@DKJaU7!L_E6C5~9rB0AAHGx#3QfEk*U_rV@r7n;# z1%q^}N?4IxtC+ng$7l2uY`iap@+1$O@8gwuG_~3dI8{TBTl)P&|;js?-}2 ziU(4LN_`-qcpyzsDFYIU2U3YjeIcQEAl0eV4-$$8(v2$hhlJvR^mCOmA^BN)MWrlA zn1>?N$0}t*YRl61D&;_G!%~NGxyAve4|g^l*iWT_2!*>C(gc+TK|%$HG5b}jG#C;p z0Hj4KTf^5-I?s zk5sw{5-I?sXEgeYA)x|5+M`k)Bvb%M`&AkV2^9d+H!6*Sgu570mkLSOXh^t=Aq`V0 z9}@0jNYhjr0}0gx5`Ia{xu60_s3wr^QfVwCR1-+QP-z?_R1-+sRT>Wo)dbQ9Doucd zY69t?N)sWWnm~%Glrt7WLN$RjNTo@TP)%aY{xejXjG~S=`z=;3LJ*W|(Cqh!O2v>+ zu8?-9GzAjM71G~TnhFW!3dx)!DJX%2a)s1grD>2*u8?}FbO|JsE2N<+T?z^13Tc{3 zmq9|g;wR%XZh*GS6L&{U(n$>4Xt zd~tUpC%mD@fHYQ)!#Eqb3D}3O7UOK-ONtn019vH6oDKXtklvt)!@wHY45XLI<5n|% z52OeDaceN<^oAk|ol$t)1B{b^oWKVe%Yd~05cd${Qee_BU=!oxifb94R(zQ8b;X}E zegrHSjbqpGlFzbrzQ6HZk@Ea&A1$I8yN$#wkE9qs@%-6rW>!3$>Mx zeF0stH?&-j-NJaQ;){%rD*lr31;v*bcL4cBTN!r)`HFtU_@&}D#wbjDI457@DOoqg z?TohK>x=^xf5WJ==(rt>dDu&ny|_0SXDGhKxJdDBMx9j0?c~|rZMydz#`_h2%lH(K zR=whOF}?-7A|LoJ|YFgf?}SIcG{20?jS5JwXd0rHZC$3lXdS!*eoTi^KQo!n7);$ zb9~~;Dl9v8#`inum)wNkiS3Lf#oY)`@8)#2gZ3xz>ETM}zJcWL;8T;;IopThy~$m$ zos9T959$lgw8vsgncsORKHm?YGNd!!q@NF;zQd*SWXu_7;D9twpOo~IP^mBPlgd2` zW_D*(>B$`mly>tZOd9tmP&%oS&fN)=G*xWwO`u$)N-yqApj@s>Z|+N=EK&tiVss@? z9#92ugPx0$YbF#b7 zpn%=2my@l1a-))y;U7yyrVP6|2U(hYAGTB!eTHvheg|7x$?G%XPV{o7y%Ae#-TPdG z=dkKu9I!uxPit#^^6b#3@Hq~QRnTnfVqhOit~ZosJIzTSx*l)nI|SwgU_=BoP~^1E z)`xNgPo#kyEoGTEl&pyU8L00w_dySi9v~BYIc@0UfnyXg3II+Aj;1TYI01M!kZOG& zj3j`+P{ddR_$qKO1x{jIhl)bod7mE6ULf5QeXx##)xVE{A5-8gLL+il2}S`ut}*b< zMA>F8tl#Y0AqYDWZ8Q>YcE||>#<7u8A!nNtp~WNMA(S0Gi{gM^0}7BzG>B%06JhY1 z9ZUpwg5iDNNWfymV)muMlo7cMep;J|()6EW2i5tASw81`57Tv?5K*Qz{3#8CR~V30 zs8MgLQm!?8zbbf8gv>^mpH#tvA}S3tJyb2{!h<5GhWA$m4~m=`K2;SwD53&4)2~tm z4~m=`p3q(5!Gj{FhA-1Fcu+)j3ipmG)w(LONfkUO!sr)a-c$t-iZJ+v@+VdBpa^4M zCtcu<6aFTz};3LX?;+zaIyOOC>WA`E+>yrT*p6k*g0 zWriNLSXV`=Rl$QIOpOrcW>xT@2s0xn537O)MVJ^tc~KQSDDtuLmMVBqcu<6<8}Y;~kTl;YOCss%s@%jaBpyKxQ>B4hNGQds+|2a{O1&z#aQ%UDpDMR< z{ekj?Dl54DK>33zw{iVJ5)!|Gv@W85Oj7@VQ~hmf325jMu_l;IpFgIiAmWiUcs-J{ zu`TKVe)L&*vB|GuJD2#e4~WBGa1gQ0PW}uEwMY?f{s*3!9FM|b-f6=#lk=2+H=Vg& z`Q3YnZ7N>sh$8S72o)^*djs}S_|mW5ojH-ew&M3 zS70a-kiy6NG(mT5md5qC$vKdD*QVUMaU&JWJ9L3RS{>;#5#W9Ri zfm|#Fj8_4v%Z?b!xCBU_+l?5`UMO?{vJMfRR_jW=f@Ewj^B z|4S9L%uZX~tO{CYr>*wa%2l9ccG_xN6|~GwTRlJ(w9Fkjg+;2MWp>)?IjYq7+Bz-j zB2~~bJ1y!Rs-R_dTGU5XLCcI~1YG+ws-R_dTGZE6LCfs4s2{3=mf2}hKUW1Uv(uuU zP{nzKkC^?}%9T2g@S!BD;yl8KlC6sK2p`IHRh&ooX!qu*;yl8Ka-%BFBYY@pRYA*) zQik%BDrlKe%1~ZW1uZj58Om=|LCcI%hO%1~w9F`FC*T_sW8Ddh9|;^L?_OoSvadBiA1sHB6QJx&A=8Qk9=^{eiMjmDOB- zpxmv>8m>Q3)~oUW*B|P+-h`CdeVVK1(r@$W{zz2#%lf)M8c4pcoAV)s?vFhJFE;d) z@}*RNL(c=LY3+`N2>6O38Y19MAkB!oqagx*4y3lUI~pS3cR-BfjqX=4{s^Ros5{yv z?Da?&*d6T>dIt|~W$H@0U&WXVoO=cEK|;g(MLWE=)Cu1L-qYK@JsyR2hMVE-1DC${ zwM$N#r%OS)e#)}TSZ%`S4NMwtb{VHl*lEeEF5|Tcn_MqpCTJ6Og(?%Z3Db9Tx)f>? zwn)QF(kAR)RVHf__H$K=vJls4PiH9JK z>oT(CHIVS`jqR zBgA(OqR{A@cY`r$o?vQv{30j&gFUs-#;SrjuhZ|8tCFsTmU^8WWow~jtI|sgZMh!R zTMO+$Rr>h+PRpOKM`cLMk59@#S{1~D@vdbu>N|!jps9QiUq|CZ6zt~&Y)U$1OKpqrhUJ(+Fx>^<&1lM8~NQcIP#LD8ZD0!0h`V+)lkSX64!QlZ|8N-bK7 zNYT<3@rH_WKHs%|@B6NOnIxX)oTq)xbM`Z9*7{x6{q|eGb$M5&Px~!6W5IDs))Pdw zVAbuonvT1d|LO}1`ag^7kmJ6>e`$*r3_R`NpU3dvoE@T{%zqsYSg_@E2XBbs3o{PB zg8$mGUU1P34!(!~l4KXW@zV~DPY^A*^e4R<1HJ@SI>H5-(VLI_CZNl*rhJ~Gh(Xm& z^O3)dK?x1K#({E9r&boUIWRuNvVesuR6Tm-VrHQVcpydX3g>uwdGnF40sL2$1FJ?J zENgzRS_-(P*EZjb^V4#^W?9qY*f$=*Q>}VKiEx^ZEPzosO|N&sUI(bJLo}V}znht3?Va~jk<~jXMb7vei=ijcL{e!C+(xxrs zGXB}KX5nl;JnEU(JZIIcGXbW*9)Met%OUs7^SBu)aP*8Qn%Umuxlf$kGw=IX&pxE- z(55wHr?mw?^3gMI`Rr*h9*`nXVa|&Tbk5Cl-K?k|0Rvx$%U{2~qAsn1W z6+|?57MVJfLHrOU%*V((R4E{@^JX<|!I~dnN#kKDcr@VQ7JiFmg-&e=Xi+IFFm4AN z;S=nOyHhW6}VhQ-eO$=)enop*rs;ydFQVpO*xGVw%KRvksay+Y-8{DpVIF zFH_E%bv9w2+|;COzu{OC(gA#mOJR$$;G#ISCdipSwe1PIC5)*fMSH%*Vbm>ghL%)e zU4oX*YkKzT*=gx5o7eP03cI94FSpW&qoZch!kd0Q=OF$wtpf1Zabc-2$;S>m)=uUN z!`*!3vtxX+P|2OD?|pXgDaGPMrDNy#?!vM&+DA(h9p&6mo{zJ3EM0!avK1?qEIX}Z zYqpZ>*j6fSE9CeLZO6c9wwxR8IM*Ti3X_$NWqp|)OWK#VFA0!+h3v#gsXP{Sbn?N) z{16}Y=XT@@eBHj>5&Nlh!;(>|Xg(Rc_ z_x0fJa&{*l5sv+yo8#l# zqAOP@#9RYA87iiXu&*>;+L0^Qmy==QQ0!CduHb7cZ&a1HX<+l#x8#PDc;wJc16zv2 zrH$Dl^{=9LsLpaZtEvUq($k--jF*aZ?>L4uBbx?%p~e2z(#l zeoGHknpS34(nOeTw)9XWRb;}>SsJ(WY|8G)Z_BFxz!RsVt5hf%(A5(Igtcj)J3k~| z#;!OMt~I6!r@`j3<)P8;e1$P_cYUFHauV65fzAn<$JWV-+;EuXEj{aLw?09e2Kx17 z@;Juc?ABaTwSC#*)D@dA4vkJh?K-z}WP}fm)3}QjO#p-*_cuD!Mh*Yg#V*QIkJkG( z4fIf9yZW*dYL%`^2lHcb{ch>$Efu%*m2;KK6w#{EN1%^ZwXID9UBqXNgpbA@7`2cV zguGLlTAH`Dv`lNC-_qQA9E7}wUmw8NzN@FB=lq@}t*aLQtfi^-*aJ7UG?Y1*QB_*l~V<(B4Ym(OciGVSC;TADXLFzw{|EiFGGyyFi19F(Q= zT2}w8Wy5VPt2eZ4SZx2BPion)tfhTf%ZAp|TiQ3btZv?YYfJlSu#X z>xC^Vf7)`|hL)9!^}o6KfLmLRXg>IzEla+MJ?}Fu=ic11q`CCYmUEjAX$LcYUh|>t zV8#HpH6J?Ca#Zv9NXyLTgFl1ot*xu&Y?sp^VLHEM=BfDG-QF_uo8M?Tt9kdgT4sKu zrKS0Gt@pH?-hAn_lPSa2a?9zI!iJX9d)ZEN;E1WoAdq z;&-&nY@KX5t92oTazo1zt)FN)YTDBIEk|rK0LU{O6RNw~qx){2=eiPW zoLK1Q|_0k%21 z>SgEXlQf$~D%M+lKu^H>r1N#biwqS%$55yGA{eVKIeW4Cyz?QfFLX>6q`*a={|372bIs)?2*OiZS{ENqW>Jt6!GB5lCj9 zjmgpDAocCDrf5Kx(VtG}-K4GP;=EMWbo9SVoCJR{okN7HU;Gb{xIPQJlk6XLHU>8hD1stj|e0!TAqC0Afz?;L*1WMyIu zSt8P%rM(T!kxpy4dD6=g(N7N(Ts1XDx?36WNW2)xjU(?c7dvSuk{)mi)1?!)1}3r- zxkz8L+0w&gk}bH2Ca=lUbL+K9!b6&J5>%wG`fM85I5{DS#)%1$&h^($7;YeO-?`@> zvPPt5qWxqU=^nwBp1PSQ(utETJtpQ5bZKXIp5>F$66qau6Dc)`M|y}m%(|qCNMB5F zGDa#kMtX|LNvF(~k)G%FnHdEi4%ThaB%`T35#2$* z=sI@*Q83bj-CKG#kBn4u6Old(L_~?m6X{KCFNby(^4p58iuFlAA{pM6>nY-?5=p&M zBcw&TzZy*7ksiX%m~atE?_%?Cqz{tRGyg8G}1YdIJQEbWl^?}LI02| z=ZB(?x{3%S`$+Gy`?kNfR7Q&P1*-3ScAeW-$}|4x9edBwIXO`pDwVDCxw3r1F|rJr zi1aZWS4k$WM|ulC@R%LXPh<=EE3(Rx^DNzCTr-h2QMdG{7pGEpq_@f$`y?_)QY9w? zRHP4zk$EjhB7K+1N-feoEtd*E7;2!4wA+#RROl4!hEh3y1G)bQqy^d4A*0w&-4Z0y zBYbg-+{}`%GL$WLmBz+%#Y)yS6MYTL%7MjM{tEhsOR&BlqvG_dJ;c+HnwxNKqp{cA zglk$p8x2`d2r%2YrF3q1iU{Jo)u*lO7uhDRNCWq#cr@Oa-JWATv^$2eHTXI-Bic4< zl8&E?cDmXc&8ZIHaTV)X=?tl$;rvaM#-jpt7;P<)>amDm@D#FH2#&k4K-eW6R6Gdc zmvN^~Xk1^&kEg@#6ii*^Ql-M!S1A>^F48z4^mG&XY@utiT$q9*Mu)@8`blJ7r*CXT z?W2?jMsqac1|!pYG%QmRNk*9=%*`vi8hZD`O#QsRWt_;I1}Yf5x=TBY4bbXj4bZN` zHNbBf-dU4sU_twoeM?+s8>4NUgjbtYaDr5XR%-TYhgI_i9=JD)XCoQm)%Po%z zlS8zFzynT=d`@htlYTb&G)>KL5%O4NuR#nDU9N~2zE`eJt!P;34Iv8doihxkO3X{k zCP{7;eN$;lua{J(uF9Kvs#|nbM^{HqVymO1k=2oiD-?jzC_-%K0J3#gc3g|t8V{Q$ z$F@>_oyFn4$>Pw&WY$Ss)q!s5?``O-FRNY{M&vf$>d~9nPn&|YZY-NGbPf+AFR2p4 zz`EH>9LpB!gB#qLpPO^-*?tqd zq$)2ZrHXLTC?lAyIUCWp6bn?$UKlzHJF~mv?pGBwC6O$)*X~xYzlc4kafJ73iaPNW z6eiYs{=qU0HLFFkc44c-N@;^MtEp%k^TTZ0gjBbj(1=*8rE-g?7_Dr^?o^TGSbPg= z%wQE>?~~xteZ=a8M%s_3;)qA_sp!-!a_Q7DK&sP8xn)crf=b>G6pb0y=C)-uSv96o zo$Lx}U}#KVre&=%P5Qsd{d%2aO8_E;Z#*z(z38p_c zR+`A!dN%YOyG_ku747^P$_K`w2gKWIok&%n%>TRhO3zNRmfcGLDB>h^g;7^UFj(5g z0Sracx38c!F-9=&I}u^Z@DMM$&+*?6r&$89`i4BK^z#gmuZ8>u}8P`g+MMw#1) z!!1tNYA(>gGj&LgFW9DcE401(fY?X@(N`)@=-9zN$XmuM`QcnF@HbFo^kzx&P|62st>>A+ypa8>cg+dret@UCu1mirsQz? zN4nwOb^dW{-Upj?jD2vqh4wyptQ+owi(EC_iE=SCq*%BOX_oqiw738o5<81(mkmi< zirb4`h0u_UH=CfH!lF14D{T@rxC#0Ds3no3~YTKJ^enp>Nrw$(!P@4 zfYVC9ItLB-;+oheW5Y}|2r$it6C^jGU1C9$dFU&HfrVE=eHk;PE^TSRSt?_8oeR)M*Z0wO>p=~ZU$R_G9CrG;bk^C2?3Zg~b zn4BJ}uJ0{g=65HeryweH9S5zAwGy#>9*Mf#UPiJnj(8fMw~R6ZXqzl9GU#v<=VYYFm+wDsA3O@^8iR%tVx#$d#?#Ek?GT{0M-@#*kvgiLZ=a} z-MqOsVxNsr#mRZoi1io$yE=QFP592SD#~~QX{X(elp;2^-8tMUAX(a??o751t*k1O zTX$%SIg5B1Wfc83Y(<_CULqOiNpq2asF>d>jU0j4X$2Cp$>UauXd984kpi29QB^Th zKyh9&LnAR`*@j%ob-c{sf&4hT7_Jp$CdS5>kdU!)XfYdz*twWQSSUUb9#|DIKHFhh ziaj`H0#^#HiYD?CERs0zLvz$dwsiuIRjc;cP^rVOipI2uQ_t+ScB;{h;2^Lie^~Iepke`h#ijN5vw8> zPOvj1q6;d((E!}?SFy=qzBIyk-U=m#zSXH}hD(#uSD}#Q5R2=SJCHlz>^6>75sOW? z3U&dY3HQLNh&oKq6X2sifLBG?SZORxYwACd$x<|3*kwzl(6wG7hFlq+2~S$@dQ*{( zH%xNeGLRDroLNToTx2IWE-)9)0NEU-;pIo1-qsZlqlYb<9q1J5zA!Cm4d_OZ-qIaK zkG5txjpgPu>ezLj%(yB11_f$pA@V5W$2Gqt&9F`ZaOhxQU9V-7yqRF;)5geNgK+t< z_%mU&D1kXWB1NBivf5P8+I%ThbWwpa@fUO4xuuGmk(_e}9lfVwGF-MuM0C)#%2+hy z1iOK%q zhr_CWcyeYOB{nV3V9hc{Xi%M+DC*ikT*iKHoT3hP7kzQ&D;qg^0Kp}A*bCa)zVnTr z=uO6Ub+#c}SvN95g=>DVE(OVS$43+^qD24O_xGmh0YTtkGJcQSxdnHjQ?G^l4%L}##bVZ#)Fn;%hc6)U#kiAvSC zyxat9LOECAV`zJfREj+|udGBIy%;X-^xeS~l-qZT?@_2@1?MU=@)Ju!S`Tex%4QEZ zQ*lRBIZtOPa{1hb0;}leAnUcR4l*i6xhhPk3d7vl_z0utN}FV6$nWWQGCxu(Gm+< zP)<|a26tklr`Q{J9|o6R&MgvaWwbO|7~V3@eKm`^bA%z;CE4XT;;vVvGQy2N)pjMy z(sBIuk8gMk-CEl*dk^;%O*s)2bGhM)E>~%eNK3V=kjoY)$5VB%OQDqoLLf6RVU$bT zR3y>xZl;U;P&|#1W;eTO#NiOhP`pfr`G z*f%cxdp=(Zv?XM;vEH-^dW6#|SVOEG9bAKT-W&G&%t*O(MXs18ZZuUE_-+*-(lT|V z3PVw%S?F|g0*%;6NvR|ynPB|F)t451&(BA8Y{L~uScFPC9k5g?&~L05VqrQH2+j@V zQKWLN&^C}V8K#_^+L&Y;E&ZiZt$>@U5%zrjIiwTzqy$-?FIObg*s3M2|2lMjj4tEk zRoY0oG|r9{W0?;z9yI*XbXQ3J#H9wSSV|wrZJXpg+!)u(CJ-Vr$gPMq*}`Ub9=_rf zy==ND#NJX?U0MY>-kan4Zp>3UKbIRvxZ06h&k!KtAc-eSUi_t7S`O+RO}v?kuKtCy=5S1Iv{c~8uttWRm9?ePcIka>k?_AId?U!p}@~HzPI_X>GrjFmoc~5pOV-%cB5swF&>SU;ZivB|NR;=~$X^N~%@$kTCSh}5IhVJRI8T1%q>ox?!YOHwJ7XkxccZ3GCHI^Rec z)F*v+ysg6C)r6gY36BlJNDk%Toq~@@!GPU^(gek^3Q#c1X z&D5qe5wVcGZKiW+O69FU^zxJmcQKoWu2KoEY9nuzik(cEOxN7pjxr<2B}olv8K6;7 z^Qk5T2WTrIc6)7o4B7sJ%f7o$+5ef-q@Sdf`4E#(o3BJWIaM33-EE$n&VvV@Ww1D zmbmCUbbOhF^&xjk;yPehvr1!_tD55;x2Sq)gh?yt8_K$Z!v0Z0age+peKz z!s0U^PMjyPS&dk#+H+0Amj5|q4tKgf)|_vf)MiG!C^CTt!G8ntd?CkqKess%&oYJG zezQ+ifzWK?AR}15=w)Y?xZU|N_K+AgB}GSDIX1*IC)IM%=3jJM1+m;=YgUg8e18xygHG1cZWD^BVGU`{f>WJkMYvmx|m<8m|Wym8}C$XWnHf$)>(IeQ>TcAa`B#d6*ArEMbD0kVp|-7pFk2w!g9*X zX(X-*rpGXx+#H8rN33Qxg;u`$>eX;=h$+sieEZehm}uR7R+TYxs&gZrp;1&Sg{8qe zw}iIXvrz(ARF>YZI_LmySB-+9Ir^qzRTAS(rg&qCC5qEgk7ajBm`V{>BqS?jks@I& zU4n70ayVD?0U?H^0#_L_%1I6wZ*+ytD~r^4)B)8}+#*rJDAg&i?AzvOpKeO?*)*I# z-d2TTs}X$&%2;in(d`^DHymd?GwF0BZtG7np4$tMs+)N2o~Kgco!XjVK%K~rjl24Z z0Tksa!451U3UL&N5x+n8)=65c)Pcbk*MQW~#?j3_GpQ6kW7-iN2(9&7LkoRbKpPp( z(Km1xmBVxKcAFn7$hK1k24iwE^Uxw#TWj+(M6E?}O*`Qjvn%a)D{q(ZLQW!1KP3Ex+x;;;BY$h1;6;M65(s)UYerv#xQPs3JNTZ&%vluDpF^&>PBcAzRKpLfOC z*EHOhEv6$mt<`Yx@GT623AeB+8h2QSaR4^ebJD9M+ihXG02X%w+ze0yzcG{l=O6T2AT z*uklJjfh<_-YzPY{RMKan{X&w8IywnFFWpQ4Ah{xS8-zzfo-S8ay`kQ34s9z4h{3e z>|(MT7C&(3&*efx$f&}>wS%hC5d*C;o&U}%jN5g#m4Q~+7e90xV z21jv@i8nqv?3S;}PO`>;%2eX_3>;OrAt}A;>)B&=BhR-k#T4J7^B8tb++q;9suH

nfY4^GlCz*1|cUXB*a=rM;K+!12PQRK;$EIM>TuVEoUW!Z$!lwdDPh0(qit<2Zk84g)iy1bg*)n3 z8m^6qP9bdRV#Vr8K{8>!vPIikn~OQlvP^h1TBLY?1G-xQ!(viu%o49gI^+B@O8e2t z?M#K}i$-%SHRDqrZbNEtXQ^>~PIE(M+%tDQMYmIyB-U+G=7xD+kykl15wpGQcIVO? z%G=TKTQ||7DN!-{>0}@`!A2kAo!_{-Fz+rRZ7%lE)HJS03t6)qmZ&zgJLnwlpf=-- z8+Q9$5+og^NwCJ4w*F*HP!#b9UCU`YO0oE zEsgzjD{>#Et4p`|cVnK{1Nbq{ySXRe9ZnCY`7h0A`reW7acQJjSH`Z+{Yux@DN%Zm zX|%rE@fw8s)O!Bs$_SQj{7gJL$TcmiPp>0Mx2Dqev^_tL3|pS4fQLc;cCPiJu=B*9mZ z+ZjukZ-UWrgt|oIq2}9QaS(4cA|R>KU0-g5b1OH_@rCCJy%*n3F17$XbcwCAoO&c- za7(WMWn49Jrd)otIFt@Ri06KrE^R&;Tk{2&D`*aCol!9U$4g5sKK*2oV&dgz+CS@Q zaUV@7zH6}qU!K^igZ}J8Z9}gRH_o^cr87!#wr7!vm!FOJ8_{BaVOYIIkKIrmMU1{s zXutk(N_0O;Glc3ewbu(Hm-<|Us%@2=1Xf?_HVDMhRdQN{WnW&yi8dTn-l_bRp%-fI zA7=>4RCM*r6%9>&0Obe02)QH1tmmaNAI1pdpl{B$#7_|${m`D5^pIlR5Y?E(%;j5- zKSYv_agyo1uNk=T*pEiGuWUricrIa@3SaHH;wNRMxZEy=M(Jo7 z6*O1&mstGhSX;(FA?8QgVLmm zcKRh=TCVJN)E8`wtSVWKYP%s<;+C|S29moJ=;Ag#Q1wF76^UL;mgzb&rc2@dx zZe6AXBIn!IqUxd(q$%#TkDe1J~Gf8-iK#RA|my+*HmTV6MsWatG zfbr>djWw(me`4P!q_(9byOA|jo7q?0CmwZ|pX~YIliFU-h>^}XTa&eMtagbvN)1*I zaQmGG`$x{IP&SbGS7waOy@IFdDGtu3A_*;-+B`lCwU*=#+;i?fIQzgn=$ka9VGf=Ps1A+tp$NoOM&*#`Ofpj^yDGs}7cYUX(~0 zyZ19f?;T*P3%#DYjytF8hmpEb1~J{;=hnpmn~GRYp|NK3lULUCUtz=-YOOnN%oQg& z!jK=2cRJbp^>U4xgN-@CQ83qHlp?1w!ZA7S*2AnicdCS3>+{KVGqkUXwBfoIT;(^&Rm{GC*nE3sz!yRfaXOp3VG?~PeN_#0TmH5EL!jNaIA|;6n(t!`(glceFUksM~v^wCWL|b*l zSY$tz8>k(X)woZm`HM*$TdgA!>4a+n@q*g-BJOwuf{&J2n$(lIASh2KgM>w_d3Ki# z*gogAQ>N>XY3UhnT0UBtA*b6ZBFEj&W}rJiJ}}Al7BP@qiSn_8>@qurxR11}gQIqG z-{-`DvJ2J#joRi+Mww*9v;LeA{87xRiI+1ML+;SH@w$Qt-Dx=(*6w9krMf)Wac;k= ztkZy{N=MWL{s2nS=hL3cs$35e#S&&#a8-lxxSd}(2BJY=5EuKs(V|z z_Lrbtnfo=7ZkM}JuR(ALOBPw;a?rU50@amKCFqBI43yicZXr$Hk81`XRM4x~f* zRYDB08Jr%7cQ-iezDAH?p!658C2_H~3)WWdUbaV1l*V~n(ci`Jf?fjpqdwYUrMEMY z`E!;!sj2bNHu4g!KzuYoE6*NY`|>hay3SQ$qe|4^lpEw8OJacL=6<1sv zev+cOCu=`{uybmkK1T|%X4RTzASoS-ENoYX(ylLpuX?$tiK-suJpzVN=u>+oTFv=3 zH)Rk>P3rw1xq zt>h3$xC7@#Q=dgYu*S7-Tz*7t#E{7FQZIOhB!LMnIdILJZ8bcqUN6_U?hsbGi8LWz zJ2tQk`F@t2eK97ZkOc0o8UY9EZN_w=GrO!T!l6;YqJOyhTb_*~AC=-%mg31QuzLyE&9|7^46 zE2CcWs!#4zEp>10FA1fp*(KO6tupw=q1zc^lWBtFR`uB0;m^vbe(*Cw&CqAqq|U?T zz0rKNHzp9xP_EE`rU0!E_)@X#)mC-D=Syh{KA+7E@y*t-sgb^^@LN)qjC`Oih+A(y zae2{&Gcdk`NE7cx-~qM@>33f3;bm-nH6n5nfrOD^Ke|rE=adE3RAN7NkV7?CVQn`_ z?e1plo^9<(j;Z?TE#%o7%{5M)BP$na6#Nab0w7=?3WYBLg=4rrm7yjFeJ%@ySlZLo z3U|YysL{a>V@ms=-R(y4T(#DtucNdZuT+iTs+*vV>Crig^;HMyr0?s|?zYzkCXJ$=(^?((c-$U} zJvyKh2~}FIo}xV=YPDbS2b2b+1t0Q7!7U@Zi_E=!-}u;KuUTYp7zkAkoEvoT)YwcK zsalQL0O(%3a!ZkPxdFR19=~_WVKLj1Q>5!8&jNYfaAyh0`z@|e;;Eia>+}ks#%Upu| zV>qsvDw{z<(rtun_AhcJI&TL$b+-nA_~=}?O)I>g1w!n2 z?e}lP?WiEL0Uv_1J0fN%YuhS%dzHd4y7BBju1ng&ug;~6)dcvnmCD2@=liye`ixot zy|ziCr%NhvsMXEIzXO&$>KUZms;Ra?cx`y_QcLPZck$JS(>kVWJ5fKWd^^eJH%3$a zwgwZ|VAUvaSARvYwo$|-SBqCCv>2oG#UO%YjH4yQhrWjMV@dPo?LJGy zo%$A#)s0=*@v^Vq_L91m==_A{^sXKvs;cz+*xgnJT4L9l>5491ujsac!Y6{{szeFf zX(8ZD3MRg|R#&R-q{PPi9>?64~*&)jQ&e$v*18fgO9 zUw4hL?nx0pTj zohd${VxxMR!hUVfbG9pDo^%BgCZGmET@n(fkr)^lNvk9Qve{FVwCxVr%(4Z8Ea)*rO5n8LHY!!Q%C{MAnHR+!t z;{Kk$hRj=DaYKs((Ze$=4EX|rRsrm1`R~yOWu~*^6Yd)Ix$c%Wi;P`d;MQSncRoB# zhPzkMPm|qNbfd*4C; z$p0SbHebe_yj%F|x){BklJ!%PsT8?tuB)(eBI?PJ|0nXk1pv?XCR8H2U zQ=%8H@)i5ulD~Ie-DcIrd7@A?=*xR3H}0>Kth0-UYP$XtC`j^KC%2J&4(~XpEz4+$ zDjNaL8 z+mROBn}%toBi>gCH?ff#DP=aQ=|t8!)C6;71_-L4qOsq}gsA}g&jFC3K_ zsnGpw*G)0{x4N@#ob$vbp9mcDw{R4>F6F4u(3`B&SOV8(e3$)&3Wkxm=OC`Tey>}* zJ18cSfS;giB@E+eHhF`R=C*qSCxaQHcv&G0i2BP_I88}Q=XE#cv7~8GQECZvrc%hC zWytp_WGK_n6DOe}pVbr(t>>DPhF{f}Ti1gI+&np9Gg_P}e=0Z)CJy-`soVlIjhoAL z3d22uSy3>)xZG4x#p#9@IE8f08+Vu;F6ZccZw;c6r@)YzRndFTbIJK@EPwh3Os ziy^(XrPoz~JFDfr((F8geKK^&H;j0#o@)GzkS_I8lH1Ki#?z`C(cX0!54w|9y5#M* z2-s_5HgcCz?T{!p7CF11oi~NgvdRyTE=1`$^nst`o~<8ZSlPw{p1O6S?J)I)yB9#; z%I=Qibn~wPgQW{5b9}YJcCG^jR&s|S&DUrtz8je0lIO4ERZ{vbNdHD~0P9XlnI|3G z;?eKhYRFAeOy1ZZ-NuGT^4)3^pxp_LTHab((o~t&cKTtY*2~uPqs>%|sk_}p`gljs zEk>c)dfFv5xr*r^Kate1IAk?nxG`PB9gq?~E{kd|7PEsBYC5FX^3_lhADuOOm1)Q| z6no1#J*%lAu)~`1(tu+`+c|_3gFyxPw`@a1zNQM}OTtW2tO|S$m>^E~*4*VDZ{cSk)c)c&0hau)lBUd&|Rf@ z8iNFnE!A=ORaMP<{S?F4a5N%iew1b7oy)p9q2xXd8^-58oNBMSc)U_dzjp8Z^9&|> zNlv5dd$y4;7ALvtQ0@drrQC8z4*x+hdzs3gtJPaoWEJ&^Vw_UI6M-W zID5gf4Hafi9c$r6pY3o51*L}P6ujIS4nH-E=kUY13FyCr>zZp?z^kGsnxp6$%nZKb z-HMruIUMs1KF~a!k4Cp*4#mVj@#{nPz0iMDgMSwMbC@4t{tNRWW;%RxG4nBt_+WJW z8zG$cVIIeX_yGjoHZ+=MxAFbL(YA9-C2lZ9-s)qX>thPlGB?s2RF?mU7pVDYEr&9; zM_jnq_cXL{kZ>>@Pl?;c$1@VCY2ld@*7!f4qDM*FqF16QPNLw&zj;`yOtFSi-KaSG;CGl-W!;H|Rg`|0pYL4P=5lAFCTb0s`5aM9t8gX{ zJ#NT4-uK3|5&N@s((>^9G*zUA@4f`U0d#*Egwr-6+Y^{d0V=}EDz{a^AgUdUwYw9U zvbasmu79rLW6^Z0gcU&uQA1>82Tohti|Jb94D;qaYbJYVUrPr*SI=t@&IjDYxC(J{ zGPlZUKk0j{y8SvyZ_8~SQSU}i%YPq!kWiJy|I)zW>hy0fuG;DHpg4RMeF{nM^8HoZH{kv#Y5ErKALG8;+=t;oWg?z4 zaH|}i#yy5xWhDGn(|lSO8lwU+Jc!=|rYR=M0yE10Z-Scz4puh?4TF281~(U)%Hl?h ze#+CME(FSvc;erhrOVeWUbXyf{_EgpVLRFOtdWtcaH~bjdDY5j&61^JT(dITu~Lp` zhezZ=>~9!DQC{(CEQ)^{20Aw=HA=6H%2+?)nruXU)qcuv?AF+;@ivAvR?2-~fSZGR z@$$tB)_n5#IRn=}*7J9Ve(a;qec>CY?fAiewf_FM{@|6Jf4}fg-f~0B86R#w?C9B7 z_nd#v55K?g%JVBHzVgV^^GhfF>&+is{)020CX5#`O3++P8)h+P4WfqderDK{8^))ph)@s#$e?`zt!jY^u+MD&40{)q{aVz{zs7xubv)fQr&v!GvriTk(umfBV`GjvNd z3&^(}6`|%}wIyK>!W@jzyni_6NX*fg*J56WIRxpm=Vl2jOe4- zZ^0BWW0)eQjG4gfz)-{yRTQ24_S!X9G%zE#4Z$0U?*Uh@)uKrU$y!H3Lk~w_# zlRNMH_B+}xJ?oeM_UGj%3w`rn>imZrpV@i+r@wfA_ZL6$fzHR@amzctbHjgsebyZh zK6Y|+)z;C^{qU{3|8(B@A2@sGk3RdEcfaGRwvYV%#mknz`-LYz@wRXN`th62m^|~M zrtvqw`IG1W`{$PbZTp(1F8bPwGZyY^lG^qtGz_O~5>@`X7q;~)9Aw;nw4e@2eGa@VU1=G{DW z`xpP?9Y-!d`n>O+^xQk1f9|LQ-v7(-559fru1licpMB%6djGuV$kA{9(>H#(N@^+Cw@@Iel#d9{?)qL%|@7(#GZ{`2`k$6Q``e*4um0WpPuSkMcHOVaXD@1f zc;>VZZaL&{HoZLi+RVq>k6-+yyH`*DqYuou_QYi??*87D(H~!W@J(m^;MS)m&x}5M zDNa zKsf_J^a#u;m~$}wm>lM6%=<7O!#sd_4D$obtC%AgTu;HQ$6SIbVBU@S5au4t=Q00` z`3YtQo#qJ4DVX(`OE3k@yD=Za+=KZ%<{Ow7Ft1_`rIVe2S&8Y#_eZe#}bD`IsDL7v{Z~k6|9bJcjuJ=2grQ2a+DlIhcM-0rPImhcNeGK9Bil z%nO(qboL`Kr(n*(^kWK`t1<7xd<^pd<}u9oFfU_fF_4{rS&8Y#@T1*Qixgt-c{2lMBc z&tkrf`7!2(lOrb&eCPNN@13k-V->+7V>xPC|j(7^d&?CH_8u{7e2qdfCR(};gy{=bZWjhg&E&4{ER z4`3v-AjcRig?fg8){^%D(x6!0Omz{<6WN6M87f|b zPc>MX-_YqJ`$|W`r3qA#{iiR+U%+XJJ@Q!3yc_Y%f}GjrQJZNjhB}l|Fpk7 zSmyspE9@)N!XC2t&r9R~y*?l01!-P>&pZyN1@|mbD3&KEZDRQ~mc?>{1$qCeNs@-F zlqa?(sE+e|EWbD{L3LU6sKZ+E{N7c>rmMbkum!wHLq{#>Q29jlISz#Tg zpGF&303({2_6mgT1)tc(sDhI1SpmR zeJ`1xCVUP-i89~Ee=7~x68lA@7_tz(g+atZKIb@(`WVg3P_Gb){I@a`)4Zphkof-! z(uw?Eiu7X4v#dx|oK~bq(Rk$lN$bzRhGewb;(cq+$gg9Xg}imKE2$yW?Gy1 zh_AErDd0F~+dcE!wj_yt6Dd;4{>Eo^S2|d%H0J)4dDDRR|4cgAEL`nTzd0$1{}8B3 z(jSvFi&zqaV(Ia*JeB71(00%K)wD?diAt9L$I>i~TmR9FX)R+BEaF;^G1Rg)kNQ+P zytKc?qaG#YRy=1|TITw+d_GM}2RT)chn$lzw-J;QexYxF3#b#z(kGAg%wI}7+WQw&y4O#9ia(sz*sUwP|KrJo1^G#9eG{`1)SuY!aFTCo zcdts4^h0BQKg?EQ6Nc(bQO~oya5>HSkJjMST77p6g$M4fTh;9(B-}3F@ch zL8bp=U$1|Zw&XjkcqCVu|D#ERm_Oz_WHTjhsa!nZL;g0c*igrA_o(w}AJ){gv3PO0Ka^FO3kczed&? z5zW<=jP}WzSv2Y)f#vQF;61o658xMZFAU)Mlwb(21YR1@wT_>Qq1xQNxqA(S1w7Et(nKEtuc!f{!-7X=eYN79xKyn*AI$Ssr^Dn^rgW z4eaIEpTVXU#_q%x|E*?UKO=hZdcyxX@x2e+wKJl-1uH(mR~p}2%>%>Yl$Abj&Ps)RdnB7;BIb-?iQ?g1b@)@5SkwB{Y@0z|BL9p z8^K)VDSrH0R0;L zUru;O1J8#pSaiXy(60n8K%Wgf9lBuA1^<>7zJzo=Jw1A`4g3oDwAb|1^yqHE;uHK0 z=nH|*gZ?exuRs?py5L8k4+38Y{ZZhDp$isW@TZ_(4E$l}p8@_W=z>KTd>8b~fbW5R z5AdDP1&c2D!_XH2zZ?3kz#oJzSaiYHLq7%hCg^*BuY)dFbir?jz8Lt=p}!OO3h07G z7d#GqHSiy&L#>=obU`K^H8#;FZuf08e`* zx^FG;a_E9Z7u*iL7q}Jr8sL+m3l?4QLg*`jUncz3z-`b4i!S)5&C!E>!0e^n*9!b! z&C%V0MHl=d=*xlGd%G_JW^eLt!J-TP9`xD3O5Y4%_9O2WEV|(Pp+BYePk;VGGkRF) zf<+g65A?@@XG4Do_%7&zMHhT0^jCn-hW;7g+n@^;UGUA&UjSYV{Q=+`p$isW@E>8+ ze~R!OO}cI+omYSxg-@{f1aCFIP4Mjk|GM8q_gx6?Jop5QPw;x^3xK~sIQ_tDpbHjV z@M?_WYlrU?`rAvSYX!JP@Cg>5;DyF_xY7mwSMa?K++3y0_yo^3zL%#_&fr@auOe_S zO{1KRPwAZm`Y`Z~&>shW7`kB51^*TF0`Ol!{}eDf;kyNkF8B`U*UoE-zB4zv@8iH9hAtS6 z+XWXeDz~lhMPNTjI3wWDwcjmR;Rx=xcnT| zf)$S7la(IIZxj7uDeyw2$I4UiT#UkDzIWlY65n)iuQa9M2!4_6AoTCz#*uf-i)w_O+09eHm~+^tinVUW-wDD=ofz31>Ap z+F%l&U}QWO-_h{BK=_Zs*9L9@e1iGi-o`)VLl<9>_znf05yU6>Ir;$AHvJj1|HkZ3 zn|+JfA2NHJ*+XV8GrPlV`jZRq1+zb4_T6UhHha?S6=tiQ@;6;|Ci`Kt-+){y_#DPa z*=L%4qS?orJ>P7`U&q&E_HQg*KR5d&v%hclzncA|*%~Vq&p(>|4`x4L_Wfpm!tA@v zzRBzx&Hh8P|G?}k%-&&k$?WZB51W0t*_W8T#cak&moANuN?(`R=a{|H?B!;kYW7KH zA7}RK%zlm8^UR)YcB|P6_9hribBYs{`qU%~Ji z^AsQBqO&hI`!cgHHha+QKC^qx-e7i|ZpK9y4&$M-StB}oq1kO_A8j_{p`*_?d#>4x ziw@svc5VJ101xlZ2V3iH@jf=sM*72Uv9SQS^Ubc>_M~p%ic`4+4U%2vK*E2FZpoGt&yQx5V^*sk2@Bd*+z#g_jQ zu;u?4Yd>e_?r!X zli{z&R{y%r=+_$k8pB^{_+5sdF#NdT3x*#x{IKCKH~eLWzu54DhVL_cui-ZszT5C? z48Pj&D-FNg@QV#U7kdHe$8zoKDV7>9G?OFbm!qdOMNa}h6~IpbKOVqe1AZug9|yiC zfGOuYZVuo_fUghWhk>sQ;D>+<0sH{)WdVFYaBl$L3%ojj?*Tp~fbRl6I)Lv4o*lrq z0lz{%_!an`pB~+jXPqT@7x-s^HwW<3z^4T;`kOnB3*aY#TLbtB;GZ3u zhW9n#e+l5nf&Vsu9|iu40Dc7cBLVy{@O1(F5b$IGKLC7b0N)S1A%O1%ZV%vlfR75` zyMS8*_)g%TX@Ahl<2K-D0{CX&uLkf0B!@mAb^hsK0SaJ051&S`M`$+@Lb^EYH@4jKO6Yj0B!~T zasW>U{&WCGz;_1lE6t=ofL{jQ8NfdUep3Ly2;3FGF90tN;OBu~8^F&3HwW<3z%L$@ zrvEA6?*{Obz<(FOPXON=z+VHtC4jj_)*||06zkJVE{i2+!eqN0WS_e1Nb`Ng#mmm@F4+w4e)QZIdAE|68PBw-Ua;i0G9eK0Sah23{DzgTRLba3An*4@}eF3;b*VZvg&!0Cxj_E`Zkn z-x_BuDbDCOlq2DpLmJSy zbL$Sy)#ldyo9oQ2yEiwOTla3*=W+4gMp-;;?niO|$lN+#{~q#l42zM=(4U=4{*OUm zmM-u2%q@NO@#dBe?=IuleIC=} zxStB#PZMJt&Y3}&noeWJ~y!ly8%II0)q3xNW(e3V4VQwh$W)t!1I+r~T z=bkL$7AIfF(hV=Tx^&sBC1x+fc7uR#UHzLIG*ItxsaO@l($iwc>9OMsb1X}O zT@hnenqzs)e40ou%Cu&uh}Ll}%=By;80_5CwJw8)4V$|MYDcO#ynvGtt*3Ks@49+k z^gv5GmM(5fe@m9FSh8}-@|CAAX=^)eG5^{;dCB6%E0!;BYg@K-*$U5n`GK?}UQp0; zmhB}!$g*eqtN;A%YygfhxiOikI(mo=nyAgggK{_c{g^rNNocYWADofCR=rZ~<+(TfyM-Zk_sj5!v!TlF zG46!&P|DWj``Y1iG%{7{c>ee>JjFkrn#{QBwnE}pvKk~5!lb8z=Xo&D?O zOn&de6_@Gb6olqke$3kb7l)DEx|K&D6lQf8^1mB%sVcPkS04tCp73@|T^NW_?)Sug zgfW&Ka@na($L|tGftOwsVWTj%Qvmyu4z2{dG}nfqeMbGxIl$Y&|GM%R=LH&`u&6W& z-ZdcBb%N!bq4h znIW>lRcyo5wsnP}ZeN>@BoWXEV21|(OPVypuPtevw|DY6x`o5g*R~yDt zgu&1i-EUz$oAwv;p)j00lGjrjYN)kgJWUwt=MU7Esgmb@nGEk1@uo$Qi|7?Fgi+OR zpC^pf;2*XyUP$wjWGN0qsZpS{VW5w4=QLlbPe)xCE*<09q1rIevAHnXh`;7n7lv!- z4bp+$(5-{IEsR&NZHQ08kVEUvQ-%Sj?MpEq~r zDj|<2UCr9!Y#rg>0UETnW10w|S*r99(^O~CH%$g$266vWJ<;r7nf*E|0Q zEO#vD%ofu$HGyrUb6Ujd9uo8GIS28-3Ac)|spCSv?nF!MSUZ_74C|5Cj`7JtC3h+h zIb}O&=R6nHv2%QPVc8k&qos)so-W~|JC%;5%gZoano_}5U;kh(?L4i%13c`^Ey$X#A>f2 z_EYJ|_l=f{xy=LZyyfPe2no0by3bD;R;+>M^r?U}rs9ev_V!V=rFe1RAz_-Dq)PG4>|MCjE@D5Cnnvse zCs%ym^j zy%%n5Th_j$vZHuX^gmpW9aSYYWnx@=>e$NDFWa?-8P>PJ-D~Q+-J;hmhBD;0QiGzMGR>3Z9N2mY?>n-xpXSQ@Nvw)FSfw|Z=e(`bD& zr~+#9Y1X~vM!$O*NXOxmEfyK}(%o-=);AxAQ~otLv{6Nl^vk@9-L# zk1609N8q2yI8&}~_7X-M1YzubeKS=UDnk8e4urAy_03da;Po1e`^9UMsn$0NL+7mG z@_3#y^#gBgDII=|G}Zd%cszWbaNe&;vifI11z}9FzR?*){dAYS=2sWSRO_4HC5)-o zHwxoM$|Fw4%an)p!Mb!zwZ4&l{L^IOxp?4X;Nm=pesy6?wZ1u(0eL+hZaK*L3;)%H zG1dB}LKw%ut^4sczq&A{THn0S`tdY1$m(Ah#$MMqdkG^6(bfcR-a=2M^q3_j$eR0yt4$CHGi zKDxou@jU5JdMN57)G#kfNNqZvCX6}w=}UY7@Jk95caRq?3+nRnUxXp=Z{Bwp$%3sm zFUrU;VT==o;(nRD%)kzRmJW%r4qY3@Fkvi$@`Q!4hz*I_VIVAzLLS9YO_^R#7<8HF z6$@h*VOXC_a%W*oxn|r$82oXGRsX7fK^S{qGu}i17vr_n!nlTXC|&%Ls@d0?@kPSW zx=Z(}^}CHQ%yV5BQ>__KVQyN8-<#|6QWwTlYsNmpIEO-f*uuD%bRbsf7lh%*b^pAa zYwJ~O=si?qn|#=uum-`&>Lm$74y`*kKvSE35ql3z8piuD`l$?M#H;q@>IwUI`Mvnp z&3}3752KcM+`Hzb2i|(q`iEbNn%2Bj9>QEP-`zcDbjkXEd^x)9)n7dI-%abA+75Z~)q@v)ea%ZN#*YIs0e)jpO<;BnH1iUYr zMyune$S|wpS7ps>+9OLv@NqdVYVHAOn&$vvo89T=c@K%WoPX2ZDd>3{;2IV9S+URm z3C?#5{Hnmi-pc{ldRT{fS7SR|1J?YZ|1PW-Y2Z4tg^!% z@OCUM)epP^hmvGV^#k4~0!#H-e=nq#>a9=Xnti~hAGQP8Kky@f zO8oTs4!jfR%c36Uz)!^B;R2)9UqZ4JZ-jKvcla00QECP-@DQ<%eb3N7_;Z-1c~j~I z+FAN*4S(Y7o_XKDdiEhrhc>ODWjD95z8Ah{-tyVgW{{K=A+Tj3^s{Hpq9xA=RS7d= zJwvP4GaOK(S~##)wJ1iUwqPs?w7D zJW5fy-&+3rVMwCA`jX4EE~0t$06GG_ovTiq=vAI}z(A^G{{NU>rK*kWhic^w+8<6m zwecy9;X)3?s!C~+&%>#BOyjmHTP&9RL4;Lr;-yf%$l9j2{G+v-H}~?IXknyYE#ux< zcFG#))CPOo1M7OD$#Q{rM6+qVWWZRhBnlk9K2cFpc&xdQmu^v?`L$2e_^Pf;BOfh_ z(^%C8^22;4V83!EHC|dCw3hG}?|bXK1BV;-H^@jb`CnqH!ya0?*gu}BS2UNbAR@{B z>W<=->?^lK8${MbFC@9Tk#M0-Gu;eUTWXel27T2 z-{X|KfBqMWW1sn(CsyL#fcYUv;nm&sb3y@j49DU6a2kjE+C%>1kyG$<2oZ;2g?<%w zZJI*;o75mODo6darV4dy;uT)DXevkP3Yj32Hx~6ipx*b>8-bUU?mI~DBTD}k>*Dk- z;f?VB8Rj#Xf5beEk#sPFgdc%91*1uxKQ~O-<02AcgpwR%MdN0BK*>4?oBB!k5SK=} zc|w>VgDv^VxFoxX>uHQ}IUxv8L%4BC^byzJr(8~e0^E~wxiVFmG<7ptJ#d<58ecQ1^jOYHweFAezyx=j}bq8;=!lD z6PCL}N;|k)Fu&Wi{#b+&-vaowb}qyBI&jqU-GceuE||XYpw_exKtCE7!RT(m{B9R~ z8iwMSzQk;aV1n;5_)fDwVD=4WzsKx%o4wQQirEFTN6o&(Y)yI!f0NngncZdfIcBRL ziEp{tCzyS#*+-hKS{0uJFWFj`$W}X+jpgiC>=|?wA;`Y#GTn9r6ZNEZ>tf)q1@I!^qt&sYYfZWb_(}p2thM3w!1(~a4*0?Vz81JEfUf~w9Kcrs zzczq(0qYsHIQ$9VUs(K#Zyfm90H$5K^7>Eg_+0GkDZn`tmd5b&&8;!GWNwYU8b5S4 zRAcbP=GGW}Hg4G5@5I1u_3rTB3h<;i;eUVNR^N@oksZ4mo{@|X-)R$uNx!F9b7oC= z?c>#>^;NOnd~th&Gi%i|VeO%)g_%TPGnI%x*7rM{^b6+j@RU~&^8ano$Ifsq%=nTC zj%EdsPI=I)o~Qc!SwPo$t@r?z+osspd99^qEVkoaoY!)@tl_s2GTJvx4{uTLjrKQQ z)Wp~3+r)KpH~2~SJDY(-wuVs|#sl};pZyIn%74x8ni$^K{zlAK8wO)Z5XSzU-)ay> z96b8Sz3*?tjJ09VRozeH-M;oWjCCL7vG4O+B?5}maV=w%6mJN;{!G*-mb=7{i`dFsrEM% zhV+s96UJ2g8-GR^6PQo#M>?k3-#AJ_Ex!}}+~fiOKg>(4{&tjF{qH&wki z(MCG@;Fj*!l9c$39kK4&>5V~>jw*dE_l?|~c%|j#M%p^VkbXg4vhLi}RA-(ZC5(l5 zcw(>R;lr3pfBk}m@$!CzF_r#W_gO|r$I)Jb0$*0o>3CE9#A^)Qxw5`9>%Y*=5}}^a&(K3o9-|(c znxta$6{_J`KPhDXmz@Lpf3{p2lwVvd`=QUOFH<`;WXh#U`;?GZTusSs7eak1Y{7HTNo6qg<+_*JRBP|+Ca-$*$mgDUXeIa!7SZJ)}rx8IY4GD(g{Yikij1;|QYt8q7B4OIp zSiczft^i&QJmd$-+ckbtE_d_=@NsxNW^S9_*K?7{t??+z)Yn>d+nAccnnUz|0Y6Mk AU;qFB literal 0 HcmV?d00001 diff --git a/iphone/Maps/GooglePlusSDK/libGooglePlusUniversal.a b/iphone/Maps/GooglePlusSDK/libGooglePlusUniversal.a new file mode 100644 index 0000000000000000000000000000000000000000..1bcdf91f1e1eca0cade9aaefde573b868c3f429f GIT binary patch literal 984556 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

NbPe)ab(X5D@- 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%0D4gd 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
n!W898KJwu;B<@Pm8HX(o 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 z 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%0ALRcsfip8eV*J!A;6cap`@C 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 YyePx$mn=K}?!~r| + AarkiClientSecurityKey + lN96dHHOKoi6mGD5NtsFx8NmuQqM CFBundleDevelopmentRegion en CFBundleDisplayName @@ -36,13 +38,41 @@ mapswithmelite + + CFBundleTypeRole + Editor + CFBundleURLName + com.mapswithme.travelguide + CFBundleURLSchemes + + com.mapswithme.travelguide + + + + CFBundleTypeRole + Editor + CFBundleURLName + com.mapswithme.travelguide.beta + CFBundleURLSchemes + + com.mapswithme.travelguide.beta + + CFBundleVersion ${CURRENT_PROJECT_VERSION} + FacebookAppID + 272257082798521 + FacebookDisplayName + MapsWithMe Lite, offline maps FlurryKey ***REMOVED*** LSRequiresIPhoneOS + MobileAppTrackerAdvertiserId + 14130 + MobileAppTrackerConversionKey + ***REMOVED*** NSMainNibFile MainWindow NSMainNibFile~ipad @@ -60,15 +90,5 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight - FacebookDisplayName - MapsWithMe Lite, offline maps - FacebookAppID - 272257082798521 - AarkiClientSecurityKey - lN96dHHOKoi6mGD5NtsFx8NmuQqM - MobileAppTrackerConversionKey - ***REMOVED*** - MobileAppTrackerAdvertiserId - 14130 diff --git a/iphone/Maps/MapsWithMe-Pro.plist b/iphone/Maps/MapsWithMe-Pro.plist index 7d4f030100..39dce75f57 100644 --- a/iphone/Maps/MapsWithMe-Pro.plist +++ b/iphone/Maps/MapsWithMe-Pro.plist @@ -2,6 +2,8 @@ + AarkiClientSecurityKey + 2mWtcnLWDuXgdKEhYLmatwh3cby3 CFBundleDevelopmentRegion en CFBundleDisplayName @@ -69,13 +71,61 @@ mapswithmepro + + CFBundleTypeRole + Editor + CFBundleURLName + com.mapswithme.full + CFBundleURLSchemes + + com.mapswithme.full + + + + CFBundleTypeRole + Editor + CFBundleURLName + com.mapswithme.full.debug + CFBundleURLSchemes + + com.mapswithme.full.debug + + + + CFBundleTypeRole + Editor + CFBundleURLName + com.mapswithme.full.simulator + CFBundleURLSchemes + + com.mapswithme.full.simulator + + + + CFBundleTypeRole + Editor + CFBundleURLName + com.mapswithme.full.beta + CFBundleURLSchemes + + com.mapswithme.full.beta + + CFBundleVersion ${CURRENT_PROJECT_VERSION} + FacebookAppID + 185237551520383 + FacebookDisplayName + MapsWithMe, offline maps FlurryKey ***REMOVED*** LSRequiresIPhoneOS + MobileAppTrackerAdvertiserId + 14130 + MobileAppTrackerConversionKey + ***REMOVED*** NSMainNibFile MainWindow NSMainNibFile~ipad @@ -142,15 +192,5 @@ - FacebookDisplayName - MapsWithMe, offline maps - FacebookAppID - 185237551520383 - AarkiClientSecurityKey - 2mWtcnLWDuXgdKEhYLmatwh3cby3 - MobileAppTrackerConversionKey - ***REMOVED*** - MobileAppTrackerAdvertiserId - 14130