AutorÃa | Ultima modificación | Ver Log |
/** Copyright 2017 Google** 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 "FirebaseMessaging/Sources/FIRMessagingTopicOperation.h"#import "FirebaseMessaging/Sources/FIRMessagingDefines.h"#import "FirebaseMessaging/Sources/FIRMessagingLogger.h"#import "FirebaseMessaging/Sources/FIRMessagingUtilities.h"#import "FirebaseMessaging/Sources/NSError+FIRMessaging.h"#import "FirebaseMessaging/Sources/Token/FIRMessagingTokenManager.h"static NSString *const kFIRMessagingSubscribeServerHost =@"https://iid.googleapis.com/iid/register";NSString *FIRMessagingSubscriptionsServer(void) {static NSString *serverHost = nil;static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{NSDictionary *environment = [[NSProcessInfo processInfo] environment];NSString *customServerHost = environment[@"FCM_SERVER_ENDPOINT"];if (customServerHost.length) {serverHost = customServerHost;} else {serverHost = kFIRMessagingSubscribeServerHost;}});return serverHost;}@interface FIRMessagingTopicOperation () {BOOL _isFinished;BOOL _isExecuting;}@property(nonatomic, readwrite, copy) NSString *topic;@property(nonatomic, readwrite, assign) FIRMessagingTopicAction action;@property(nonatomic, readwrite, strong) FIRMessagingTokenManager *tokenManager;@property(nonatomic, readwrite, copy) NSDictionary *options;@property(nonatomic, readwrite, copy) FIRMessagingTopicOperationCompletion completion;@property(atomic, strong) NSURLSessionDataTask *dataTask;@end@implementation FIRMessagingTopicOperation+ (NSURLSession *)sharedSession {static NSURLSession *subscriptionOperationSharedSession;static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];config.timeoutIntervalForResource = 60.0f; // 1 minutesubscriptionOperationSharedSession = [NSURLSession sessionWithConfiguration:config];subscriptionOperationSharedSession.sessionDescription = @"com.google.fcm.topics.session";});return subscriptionOperationSharedSession;}- (instancetype)initWithTopic:(NSString *)topicaction:(FIRMessagingTopicAction)actiontokenManager:(FIRMessagingTokenManager *)tokenManageroptions:(NSDictionary *)optionscompletion:(FIRMessagingTopicOperationCompletion)completion {if (self = [super init]) {_topic = topic;_action = action;_tokenManager = tokenManager;_options = options;_completion = completion;_isExecuting = NO;_isFinished = NO;}return self;}- (void)dealloc {_topic = nil;_completion = nil;}- (BOOL)isAsynchronous {return YES;}- (BOOL)isExecuting {return _isExecuting;}- (void)setExecuting:(BOOL)executing {[self willChangeValueForKey:@"isExecuting"];_isExecuting = executing;[self didChangeValueForKey:@"isExecuting"];}- (BOOL)isFinished {return _isFinished;}- (void)setFinished:(BOOL)finished {[self willChangeValueForKey:@"isFinished"];_isFinished = finished;[self didChangeValueForKey:@"isFinished"];}- (void)start {if (self.isCancelled) {NSError *error = [NSErrormessagingErrorWithCode:kFIRMessagingErrorCodePubSubOperationIsCancelledfailureReason:@"Failed to start the pubsub service as the topic operation is cancelled."];[self finishWithError:error];return;}[self setExecuting:YES];[self performSubscriptionChange];}- (void)finishWithError:(NSError *)error {// Add a check to prevent this finish from being called more than once.if (self.isFinished) {return;}self.dataTask = nil;if (self.completion) {self.completion(error);}[self setExecuting:NO];[self setFinished:YES];}- (void)cancel {[super cancel];[self.dataTask cancel];NSError *error = [NSError messagingErrorWithCode:kFIRMessagingErrorCodePubSubOperationIsCancelledfailureReason:@"The topic operation is cancelled."];[self finishWithError:error];}- (void)performSubscriptionChange {NSURL *url = [NSURL URLWithString:FIRMessagingSubscriptionsServer()];NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];NSString *appIdentifier = FIRMessagingAppIdentifier();NSString *authString = [NSStringstringWithFormat:@"AidLogin %@:%@", _tokenManager.deviceAuthID, _tokenManager.secretToken];[request setValue:authString forHTTPHeaderField:@"Authorization"];[request setValue:appIdentifier forHTTPHeaderField:@"app"];[request setValue:_tokenManager.versionInfo forHTTPHeaderField:@"info"];// Topic can contain special characters (like `%`) so encode the value.NSCharacterSet *characterSet = [NSCharacterSet URLQueryAllowedCharacterSet];NSString *encodedTopic =[self.topic stringByAddingPercentEncodingWithAllowedCharacters:characterSet];if (encodedTopic == nil) {// The transformation was somehow not possible, so use the original topic.FIRMessagingLoggerWarn(kFIRMessagingMessageCodeTopicOptionTopicEncodingFailed,@"Unable to encode the topic '%@' during topic subscription change. "@"Please ensure that the topic name contains only valid characters.",self.topic);encodedTopic = self.topic;}NSMutableString *content = [NSMutableStringstringWithFormat:@"sender=%@&app=%@&device=%@&"@"app_ver=%@&X-gcm.topic=%@&X-scope=%@",_tokenManager.defaultFCMToken, appIdentifier, _tokenManager.deviceAuthID,FIRMessagingCurrentAppVersion(), encodedTopic, encodedTopic];if (self.action == FIRMessagingTopicActionUnsubscribe) {[content appendString:@"&delete=true"];}FIRMessagingLoggerInfo(kFIRMessagingMessageCodeTopicOption000, @"Topic subscription request: %@",content);request.HTTPBody = [content dataUsingEncoding:NSUTF8StringEncoding];[request setHTTPMethod:@"POST"];FIRMessaging_WEAKIFY(self) void (^requestHandler)(NSData *, NSURLResponse *, NSError *) =^(NSData *data, NSURLResponse *URLResponse, NSError *error) {FIRMessaging_STRONGIFY(self) if (error) {// Our operation could have been cancelled, which would result in our data task's error// being NSURLErrorCancelledif (error.code == NSURLErrorCancelled) {// We would only have been cancelled in the -cancel method, which will call finish for// us so just return and do nothing.return;}FIRMessagingLoggerDebug(kFIRMessagingMessageCodeTopicOption001,@"Device registration HTTP fetch error. Error Code: %ld",(long)error.code);[self finishWithError:error];return;}NSString *response = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];if (response.length == 0) {NSString *failureReason = @"Invalid registration response - zero length.";FIRMessagingLoggerDebug(kFIRMessagingMessageCodeTopicOperationEmptyResponse, @"%@",failureReason);[self finishWithError:[NSError messagingErrorWithCode:kFIRMessagingErrorCodeUnknownfailureReason:failureReason]];return;}NSArray *parts = [response componentsSeparatedByString:@"="];if (![parts[0] isEqualToString:@"token"] || parts.count <= 1) {NSString *failureReason = [NSStringstringWithFormat:@"Invalid registration response :'%@'. It is missing 'token' field.",response];FIRMessagingLoggerDebug(kFIRMessagingMessageCodeTopicOption002, @"%@", failureReason);[self finishWithError:[NSError messagingErrorWithCode:kFIRMessagingErrorCodeUnknownfailureReason:failureReason]];return;}[self finishWithError:nil];};NSURLSession *urlSession = [FIRMessagingTopicOperation sharedSession];self.dataTask = [urlSession dataTaskWithRequest:request completionHandler:requestHandler];NSString *description;if (_action == FIRMessagingTopicActionSubscribe) {description = [NSString stringWithFormat:@"com.google.fcm.topics.subscribe: %@", _topic];} else {description = [NSString stringWithFormat:@"com.google.fcm.topics.unsubscribe: %@", _topic];}self.dataTask.taskDescription = description;[self.dataTask resume];}@end