Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISHttpUploadRequest.m URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISHttpUploadRequest.m?rev=1795295&r1=1795294&r2=1795295&view=diff ============================================================================== --- chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISHttpUploadRequest.m (original) +++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISHttpUploadRequest.m Tue May 16 10:33:32 2017 @@ -232,7 +232,10 @@ const NSUInteger kRawBufferSize = 24576; - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { [super URLSession:session task:task didCompleteWithError:error]; - +} + +- (void)didCompleteWithError:(NSError *)error { + [super didCompleteWithError:error]; if (self.useCombinedInputStream) { if (error) { [self stopSendWithStatus:@"connection is being terminated with error."]; @@ -398,7 +401,7 @@ const NSUInteger kRawBufferSize = 24576; if (bytesWritten <= 0) { [self stopSendWithStatus:@"Network write error"]; NSError *cmisError = [CMISErrors createCMISErrorWithCode:kCMISErrorCodeConnection detailedDescription:@"Network write error"]; - [self URLSession:nil task:nil didCompleteWithError:cmisError]; + [self didCompleteWithError:cmisError]; } else { self.bufferOffset += bytesWritten; }
Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISMimeHelper.h URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISMimeHelper.h?rev=1795295&r1=1795294&r2=1795295&view=diff ============================================================================== --- chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISMimeHelper.h (original) +++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISMimeHelper.h Tue May 16 10:33:32 2017 @@ -38,4 +38,15 @@ extern NSString * const kCMISMimeHelperD */ + (NSString *)encodeContentDisposition:(NSString *)disposition fileName:(NSString *)filename; +/** + * Parses a WWW-Authenticate header value. + * + * @param value + * the header value to parse + * + * @return a dictionary with the (lower case) challenge name as key and as the + * value a sub-dictionary with parameters of the challenge + */ ++ (NSDictionary *)challengesFromAuthenticateHeader:(NSString *)authenticationHeader; + @end Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISMimeHelper.m URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISMimeHelper.m?rev=1795295&r1=1795294&r2=1795295&view=diff ============================================================================== --- chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISMimeHelper.m (original) +++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISMimeHelper.m Tue May 16 10:33:32 2017 @@ -91,4 +91,109 @@ NSString * const kCMISMimeHelperHexDigit return encoded; } ++ (NSDictionary *)challengesFromAuthenticateHeader:(NSString *)value +{ + if (value == nil || value.length == 0) { + return nil; + } + + NSString *trimValue = [value stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + + NSMutableDictionary *result = [NSMutableDictionary new]; + NSLocale *englishLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en"]; + + BOOL inQuotes = NO; + BOOL inName = YES; + NSString *challenge = nil; + NSString *paramName = @""; + NSMutableString *sb = [NSMutableString new]; + for (int i = 0; i < trimValue.length; i++) { + unichar c = [trimValue characterAtIndex:i]; + + if (c == '\\') { + if (!inQuotes) { + return nil; + } + if (trimValue.length > i && [trimValue characterAtIndex:i + 1] == '\\') { + [sb appendFormat:@"%c", '\\']; + i++; + } else if (trimValue.length > i && [trimValue characterAtIndex:i + 1] == '"') { + [sb appendFormat:@"%c", '"']; + i++; + } else { + return nil; + } + } else if (c == '"') { + if (inName) { + return nil; + } + if (inQuotes) { + NSMutableDictionary *authMap = result[challenge]; + if (authMap == nil) { + return nil; + } + authMap[paramName] = sb; + } + sb = [NSMutableString new]; + inQuotes = !inQuotes; + } else if (c == '=') { + if (inName) { + paramName = [sb stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + + NSUInteger spcIdx = [paramName rangeOfString:@" "].location; + if (spcIdx != NSNotFound) { + challenge = [[paramName substringToIndex:spcIdx] lowercaseStringWithLocale:englishLocale]; + result[challenge] = [NSMutableDictionary new]; + paramName = [[paramName substringFromIndex:spcIdx] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + } + + sb = [NSMutableString new]; + inName = NO; + } else if (!inQuotes) { + return nil; + } + } else if (c == ',') { + if (inName) { + challenge = [[sb stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] lowercaseStringWithLocale:englishLocale]; + result[challenge] = [NSMutableDictionary new]; + sb = [NSMutableString new]; + } else { + if (inQuotes) { + [sb appendFormat:@"%c", c]; + } else { + NSMutableDictionary *authMap = result[challenge]; + if (authMap == nil) { + return nil; + } + if (!authMap[paramName]) { + authMap[paramName] = sb; + } + sb = [NSMutableString new]; + inName = YES; + } + } + } else { + [sb appendFormat:@"%c", c]; + } + } + if (inQuotes) { + return nil; + } + if (inName) { + challenge = [[sb stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] lowercaseStringWithLocale:englishLocale]; + result[challenge] = [NSMutableDictionary new]; + } else { + NSMutableDictionary *authMap = result[challenge]; + if (authMap == nil) { + return nil; + } + if (!authMap[paramName]) { + authMap[paramName] = [sb stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + } + } + + return result; + +} + @end Added: chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISOAuthHttpRequest.h URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISOAuthHttpRequest.h?rev=1795295&view=auto ============================================================================== --- chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISOAuthHttpRequest.h (added) +++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISOAuthHttpRequest.h Tue May 16 10:33:32 2017 @@ -0,0 +1,24 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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 "CMISHttpRequest.h" + +@interface CMISOAuthHttpRequest : CMISHttpRequest + +@end Added: chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISOAuthHttpRequest.m URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISOAuthHttpRequest.m?rev=1795295&view=auto ============================================================================== --- chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISOAuthHttpRequest.m (added) +++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISOAuthHttpRequest.m Tue May 16 10:33:32 2017 @@ -0,0 +1,116 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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 "CMISOAuthHttpRequest.h" +#import "CMISOAuthHttpResponse.h" +#import "CMISLog.h" +#import "CMISDictionaryUtil.h" +#import "CMISErrors.h" +#import "CMISMimeHelper.h" + +@implementation CMISOAuthHttpRequest + +-(BOOL)shouldApplyHttpHeaders +{ + return NO; // http headers of the CMISOAuthAuthenticationProvider should not be applied or else we would end up in an endless loop +} + +- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error +{ + if (self.completionBlock) { + if (!error) { + NSError *cmisError = nil; + + // no error returned but we check if an OAuth error message was returned + CMISHttpResponse *httpResponse = [CMISOAuthHttpResponse responseUsingURLHTTPResponse:self.response data:self.responseBody]; + if (httpResponse.statusCode != 200) { + if (httpResponse.statusCode == 401) { + NSDictionary *challenges = [CMISMimeHelper challengesFromAuthenticateHeader:[[self.response allHeaderFields] objectForKey:@"WWW-Authenticate"]]; + + if ([challenges objectForKey:@"bearer"]) { + NSDictionary *params = [challenges objectForKey:@"bearer"]; + + NSString *error = [params objectForKey:@"error"]; + NSString *description = [params objectForKey:@"error_description"]; + NSString *uri = [params objectForKey:@"error_uri"]; + + if ([CMISLog sharedInstance].logLevel == CMISLogLevelDebug) { + CMISLogDebug(@"Invalid OAuth token: %@", params); + } + + NSMutableDictionary *oAuthErroUserDict = [NSMutableDictionary new]; + if (error) { + oAuthErroUserDict[kCMISErrorOAuthExceptionErrorKey] = error; + } + if (description) { + oAuthErroUserDict[kCMISErrorOAuthExceptionDescriptionKey] = description; + } + if (uri) { + oAuthErroUserDict[kCMISErrorOAuthExceptionUriKey] = uri; + } + cmisError = [CMISErrors createCMISErrorWithCode:kCMISErrorCodeConnection detailedDescription:[NSString stringWithFormat:@"Unauthorized: error: %@ ,errorStr: %@", error, description] additionalUserInfo:oAuthErroUserDict]; + + [self executeCompletionBlockError:cmisError]; + } // else: superclass will handle authorization error + } else { + NSDictionary *jsonDictionary = [CMISOAuthHttpResponse parseResponse:httpResponse error:&cmisError]; + if (!cmisError) { + if ([CMISLog sharedInstance].logLevel == CMISLogLevelDebug) { + CMISLogDebug(@"OAuth token request failed: %@", jsonDictionary); + } + + id error = [jsonDictionary cmis_objectForKeyNotNull:@"error"]; + id description = [jsonDictionary cmis_objectForKeyNotNull:@"error_description"]; + id uri = [jsonDictionary cmis_objectForKeyNotNull:@"error_uri"]; + + NSMutableDictionary *oAuthErroUserDict = [NSMutableDictionary new]; + if ([error description]) { + oAuthErroUserDict[kCMISErrorOAuthExceptionErrorKey] = [error description]; + } + if ([description description]) { + oAuthErroUserDict[kCMISErrorOAuthExceptionDescriptionKey] = [description description]; + } + if ([uri description]) { + oAuthErroUserDict[kCMISErrorOAuthExceptionUriKey] = [uri description]; + } + cmisError = [CMISErrors createCMISErrorWithCode:kCMISErrorCodeConnection detailedDescription:[NSString stringWithFormat:@"OAuth token request failed: error=%@, description=%@, errorUri=%@", error, description, uri] additionalUserInfo:oAuthErroUserDict]; + + if(cmisError) { + [self executeCompletionBlockError:cmisError]; + } else { + [self executeCompletionBlockResponse:httpResponse]; + } + } + } + } + } + } + [super URLSession:session task:task didCompleteWithError:error]; +} + +-(BOOL)callCompletionBlockOnOriginalThread +{ + /* Note: calling perform selector on the original thread (spawned by the NSOperationQueue) does not work as the runloop is thrown away (after the main method finishes) without ever calling the executeCompletionBlock selector. + */ + return NO; +} + + + +@end Added: chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISOAuthHttpResponse.h URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISOAuthHttpResponse.h?rev=1795295&view=auto ============================================================================== --- chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISOAuthHttpResponse.h (added) +++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISOAuthHttpResponse.h Tue May 16 10:33:32 2017 @@ -0,0 +1,26 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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 "CMISHttpResponse.h" + +@interface CMISOAuthHttpResponse : CMISHttpResponse + ++ (NSDictionary *)parseResponse:(CMISHttpResponse *)httpResponse error:(NSError **)outError; + +@end Added: chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISOAuthHttpResponse.m URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISOAuthHttpResponse.m?rev=1795295&view=auto ============================================================================== --- chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISOAuthHttpResponse.m (added) +++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISOAuthHttpResponse.m Tue May 16 10:33:32 2017 @@ -0,0 +1,58 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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 "CMISOAuthHttpResponse.h" +#import "CMISErrors.h" + +@implementation CMISOAuthHttpResponse + +- (NSString*)exception +{ + NSString *exception = [self responseValueForKey:@"error"]; + return exception; +} + + +- (NSString*)errorMessage +{ + NSString *message = [self responseValueForKey:@"error_description"]; + return message; +} + ++ (NSDictionary *)parseResponse:(CMISHttpResponse *)httpResponse error:(NSError **)outError +{ + NSDictionary *jsonDictionary = nil; + + NSError *serialisationError = nil; + id jsonResponse = [NSJSONSerialization JSONObjectWithData:httpResponse.data options:0 error:&serialisationError]; + + if (!serialisationError) { + if ([jsonResponse isKindOfClass:NSDictionary.class]) { + jsonDictionary = (NSDictionary *)jsonResponse; + } else { + if (outError != NULL) *outError = [CMISErrors createCMISErrorWithCode:kCMISErrorCodeRuntime detailedDescription:@"Invalid response!"]; + } + } else { + if (outError != NULL) *outError = [CMISErrors cmisError:serialisationError cmisErrorCode:kCMISErrorCodeRuntime]; + } + + return jsonDictionary; +} + +@end Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISObjectConverter.h URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISObjectConverter.h?rev=1795295&r1=1795294&r2=1795295&view=diff ============================================================================== --- chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISObjectConverter.h (original) +++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISObjectConverter.h Tue May 16 10:33:32 2017 @@ -22,6 +22,9 @@ #import "CMISObjectData.h" @class CMISSession; +@class CMISChangeEvents; +@class CMISChangeEvent; +@class CMISObjectList; @interface CMISObjectConverter : NSObject @@ -43,4 +46,8 @@ */ + (NSArray *)convertExtensions:(NSDictionary *)source cmisKeys:(NSSet *)cmisKeys; ++ (CMISChangeEvents *)convertChangeEvents:(NSString *)changeLogToken objectList:(CMISObjectList *)objectList; + ++ (CMISChangeEvent *)convertChangeEvent:(CMISObjectData *)objectData; + @end Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISObjectConverter.m URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISObjectConverter.m?rev=1795295&r1=1795294&r2=1795295&view=diff ============================================================================== --- chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISObjectConverter.m (original) +++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISObjectConverter.m Tue May 16 10:33:32 2017 @@ -27,6 +27,14 @@ #import "CMISDateUtil.h" #import "CMISConstants.h" #import "CMISDictionaryUtil.h" +#import "CMISChangeEvents.h" +#import "CMISChangeEvent.h" +#import "CMISObjectList.h" +#import "CMISObjectData.h" +#import "CMISChangeEventInfo.h" +#import "CMISEnums.h" +#import "CMISPolicyIdList.h" +#import "CMISItem.h" @interface CMISObjectConverter () @property (nonatomic, weak) CMISSession *session; @@ -53,11 +61,19 @@ object = [[CMISDocument alloc] initWithObjectData:objectData session:self.session]; } else if (objectData.baseType == CMISBaseTypeFolder) { object = [[CMISFolder alloc] initWithObjectData:objectData session:self.session]; + } else if (objectData.baseType == CMISBaseTypeItem) { + object = [[CMISItem alloc] initWithObjectData:objectData session:self.session]; } - [object fetchTypeDefinitionWithCompletionBlock:^(NSError *error) { - completionBlock(object, error); - }]; + if (object) { + [object fetchTypeDefinitionWithCompletionBlock:^(NSError *error) { + completionBlock(object, error); + }]; + } else { + NSError *error = [CMISErrors createCMISErrorWithCode:kCMISErrorCodeNotSupported + detailedDescription:[NSString stringWithFormat:@"Base type '%ld' not supported", (long)objectData.baseType]]; + completionBlock(nil, error); + } } @@ -542,5 +558,59 @@ return extensions; } ++ (CMISChangeEvents *)convertChangeEvents:(NSString *)changeLogToken objectList:(CMISObjectList *)objectList +{ + if (objectList == nil) { + return nil; + } + + NSMutableArray *events = [NSMutableArray new]; + for (CMISObjectData *objectData in objectList.objects) { + CMISChangeEvent *changeEvent = [self convertChangeEvent:objectData]; + [events addObject:changeEvent]; + } + + CMISChangeEvents *changeEvents = [CMISChangeEvents new]; + changeEvents.hasMoreItems = objectList.hasMoreItems; + changeEvents.numItems = objectList.numItems; + changeEvents.changeEvents = [events copy]; + changeEvents.latestChangeLogToken = changeLogToken; + + return changeEvents; +} + ++ (CMISChangeEvent *)convertChangeEvent:(CMISObjectData *)objectData +{ + CMISChangeEvent *changeEvent = [CMISChangeEvent new]; + NSMutableDictionary *properties = nil; + + if (objectData.changeEventInfo) { + changeEvent.changeType = objectData.changeEventInfo.changeType; + changeEvent.changeTime = objectData.changeEventInfo.changeTime; + } + + if (objectData.properties.propertyList) { + properties = [NSMutableDictionary new]; + + for (CMISPropertyData *property in objectData.properties.propertyList) { + [properties setObject:property.values forKey:property.identifier]; + } + + changeEvent.properties = [properties copy]; + + NSArray *objectIdList = [properties objectForKey:kCMISPropertyObjectId]; + if (objectIdList.count > 0) { + changeEvent.objectId = objectIdList[0]; + } + + changeEvent.policyIds = objectData.policyIds.policyIds; + + changeEvent.acl = objectData.acl; + } + + + + return changeEvent; +} @end Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISReachability.m URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISReachability.m?rev=1795295&r1=1795294&r2=1795295&view=diff ============================================================================== --- chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISReachability.m (original) +++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISReachability.m Tue May 16 10:33:32 2017 @@ -74,8 +74,9 @@ void handleFlags(SCNetworkReachabilityFl #endif BOOL connected = !(flags & kSCNetworkReachabilityFlagsConnectionRequired); + BOOL connectionOnDemand = (flags & kSCNetworkReachabilityFlagsConnectionOnDemand); // connectionOnDemand is true when using e.g. per App-VPN - if (reachable && connected) { + if (reachable && (connected || connectionOnDemand)) { networkReachability->_networkConnection = YES; } else { @@ -89,21 +90,24 @@ void handleFlags(SCNetworkReachabilityFl + (instancetype)reachabilityWithAddress:(const struct sockaddr_in *)hostAddress; { - CMISReachability *returnReachability = NULL; - + CMISReachability *returnReachability = nil; + // deactivated static analyzer because of false positive: release of reachabilityRef is done in dealloc method, however compiler does not recognize this +#ifndef __clang_analyzer__ SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)hostAddress); if (reachability != NULL) { returnReachability = [[self alloc] init]; - if (returnReachability != NULL) { + if (returnReachability != nil) { returnReachability.networkReachabilityRef = reachability; [returnReachability startNotifier]; + } else { + CFRelease(reachability); } } else { CMISLogWarning(@"Failed to create reachability reference for address %@", hostAddress); } - +#endif return returnReachability; } Added: chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISURLSessionUtil.h URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISURLSessionUtil.h?rev=1795295&view=auto ============================================================================== --- chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISURLSessionUtil.h (added) +++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISURLSessionUtil.h Tue May 16 10:33:32 2017 @@ -0,0 +1,28 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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 <Foundation/Foundation.h> + +@class CMISBindingSession; + +@interface CMISURLSessionUtil : NSObject + ++ (NSURLSession *)internalUrlSessionWithParameters:(CMISBindingSession *)session delegate:(id <NSURLSessionDelegate>)delegate; + +@end Added: chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISURLSessionUtil.m URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISURLSessionUtil.m?rev=1795295&view=auto ============================================================================== --- chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISURLSessionUtil.m (added) +++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISURLSessionUtil.m Tue May 16 10:33:32 2017 @@ -0,0 +1,56 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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 "CMISURLSessionUtil.h" +#import "CMISBindingSession.h" +#import "CMISConstants.h" +#import "CMISLog.h" + +@implementation CMISURLSessionUtil + ++ (NSURLSession *)internalUrlSessionWithParameters:(CMISBindingSession *)session delegate:(id <NSURLSessionDelegate>)delegate +{ + // determine the type of session configuration to create + NSURLSessionConfiguration *sessionConfiguration = nil; + id useBackgroundSession = [session objectForKey:kCMISSessionParameterUseBackgroundNetworkSession]; + if (useBackgroundSession && [useBackgroundSession boolValue]) { + // get session and container identifiers from session + NSString *backgroundId = [session objectForKey:kCMISSessionParameterBackgroundNetworkSessionId + defaultValue:kCMISDefaultBackgroundNetworkSessionId]; + NSString *containerId = [session objectForKey:kCMISSessionParameterBackgroundNetworkSessionSharedContainerId + defaultValue:kCMISDefaultBackgroundNetworkSessionSharedContainerId]; + + // use the background session configuration, cache settings and timeout will be provided by the request object + sessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:backgroundId]; + sessionConfiguration.sharedContainerIdentifier = containerId; + + CMISLogDebug(@"Using background network session with identifier '%@' and shared container '%@'", + backgroundId, containerId); + } + else { + // use the default session configuration, cache settings and timeout will be provided by the request object + sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; + } + + //TODO: do we have a memory leak? need to call invalidateAndCancel or resetWithCompletionHandler when done, see also docu from sessionWithConfiguration:delegate:delegateQueue: method + // create session + return [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:delegate delegateQueue:nil]; +} + +@end Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISURLUtil.m URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISURLUtil.m?rev=1795295&r1=1795294&r2=1795295&view=diff ============================================================================== --- chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISURLUtil.m (original) +++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISURLUtil.m Tue May 16 10:33:32 2017 @@ -19,6 +19,7 @@ #import "CMISURLUtil.h" #import "CMISConstants.h" +#import "CMISLog.h" NSString * const kCMISRFC7232Reserved = @";?:@&=+$,[]"; NSString * const kCMISRFC3986Reserved = @"!*'();:@&=+$,/?%#[]"; @@ -71,14 +72,15 @@ NSString * const kCMISRFC3986Reserved = path = [path substringFromIndex:1]; } - NSURL *url = [[NSURL URLWithString:urlString] URLByAppendingPathComponent:path]; - + NSURL *url = [NSURL URLWithString:urlString]; + if (path.length > 0) { + url = [url URLByAppendingPathComponent:path]; + } + // quote some additional reserved characters - path = CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(NULL, - (CFStringRef)url.path, - NULL, - (CFStringRef)kCMISRFC7232Reserved, - kCFStringEncodingUTF8)); + NSMutableCharacterSet *characterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy]; + [characterSet removeCharactersInString:kCMISRFC7232Reserved]; + path = [url.path stringByAddingPercentEncodingWithAllowedCharacters:characterSet]; return [self replacePathInUrl:[url absoluteString] withPath:path]; } @@ -90,11 +92,10 @@ NSString * const kCMISRFC3986Reserved = + (NSString *)encodeUrlParameterValue:(NSString *)value { - NSString *encodedValue = CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(NULL, - (CFStringRef)value, - NULL, - (CFStringRef)kCMISRFC3986Reserved, - kCFStringEncodingUTF8)); + NSMutableCharacterSet *characterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy]; + [characterSet removeCharactersInString:kCMISRFC3986Reserved]; + NSString *encodedValue = [value stringByAddingPercentEncodingWithAllowedCharacters:characterSet]; + return encodedValue; } @@ -130,4 +131,4 @@ NSString * const kCMISRFC3986Reserved = return serverUrl; } -@end \ No newline at end of file +@end Modified: chemistry/objectivecmis/trunk/ObjectiveCMISTests/ObjectiveCMISTests.m URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMISTests/ObjectiveCMISTests.m?rev=1795295&r1=1795294&r2=1795295&view=diff ============================================================================== --- chemistry/objectivecmis/trunk/ObjectiveCMISTests/ObjectiveCMISTests.m (original) +++ chemistry/objectivecmis/trunk/ObjectiveCMISTests/ObjectiveCMISTests.m Tue May 16 10:33:32 2017 @@ -2711,4 +2711,77 @@ XCTAssertEqualObjects(@"SELECT * FROM cmis:document WHERE abc:dateTime = TIMESTAMP '2012-02-02T03:04:05.000Z'", [st queryString], @"wrong encoded query statement"); } +- (void)testAuthenticateHeaderParameters { + NSDictionary *challenges = nil; + + challenges = [CMISMimeHelper challengesFromAuthenticateHeader:nil]; + XCTAssertNil(challenges); + + challenges = [CMISMimeHelper challengesFromAuthenticateHeader:@""]; + XCTAssertNil(challenges); + + challenges = [CMISMimeHelper challengesFromAuthenticateHeader:@"Basic"]; + XCTAssertNotNil(challenges); + XCTAssertTrue(challenges[@"basic"]); + XCTAssertTrue([challenges[@"basic"] count] == 0); + + challenges = [CMISMimeHelper challengesFromAuthenticateHeader:@"Basic realm=\"example\""]; + XCTAssertNotNil(challenges); + XCTAssertTrue(challenges[@"basic"]); + XCTAssertEqualObjects(@"example", challenges[@"basic"][@"realm"]); + + challenges = [CMISMimeHelper challengesFromAuthenticateHeader:@"Basic realm= \"example\" "]; + XCTAssertNotNil(challenges); + XCTAssertTrue(challenges[@"basic"]); + XCTAssertEqualObjects(@"example", challenges[@"basic"][@"realm"]); + + challenges = [CMISMimeHelper challengesFromAuthenticateHeader:@"Basic realm=example"]; + XCTAssertNotNil(challenges); + XCTAssertTrue(challenges[@"basic"]); + XCTAssertEqualObjects(@"example", challenges[@"basic"][@"realm"]); + + challenges = [CMISMimeHelper challengesFromAuthenticateHeader:@"Basic realm=\"example\",charset=\"UTF-8\""]; + XCTAssertNotNil(challenges); + XCTAssertTrue(challenges[@"basic"]); + XCTAssertEqualObjects(@"example", challenges[@"basic"][@"realm"]); + XCTAssertEqualObjects(@"UTF-8", challenges[@"basic"][@"charset"]); + + challenges = [CMISMimeHelper challengesFromAuthenticateHeader:@"Bearer realm=\"example\", error=\"invalid_token\""]; + XCTAssertNotNil(challenges); + XCTAssertTrue(challenges[@"bearer"]); + XCTAssertEqualObjects(@"example", challenges[@"bearer"][@"realm"]); + XCTAssertEqualObjects(@"invalid_token", challenges[@"bearer"][@"error"]); + + challenges = [CMISMimeHelper challengesFromAuthenticateHeader:@"Bearer realm=\"example\", error=invalid_token"]; + XCTAssertNotNil(challenges); + XCTAssertEqualObjects(@"example", challenges[@"bearer"][@"realm"]); + XCTAssertEqualObjects(@"invalid_token", challenges[@"bearer"][@"error"]); + + challenges = [CMISMimeHelper challengesFromAuthenticateHeader:@"Bearer realm=\"example\", error=\"invalid_token\", error_description=\"The access token expired\""]; + XCTAssertNotNil(challenges); + XCTAssertEqualObjects(@"example", challenges[@"bearer"][@"realm"]); + XCTAssertEqualObjects(@"invalid_token", challenges[@"bearer"][@"error"]); + XCTAssertEqualObjects(@"The access token expired", challenges[@"bearer"][@"error_description"]); + + challenges = [CMISMimeHelper challengesFromAuthenticateHeader:@"Bearer realm=\"example\",error_description=\"It's expired, really!\", error=\"invalid_token\","]; + XCTAssertNotNil(challenges); + XCTAssertEqualObjects(@"example", challenges[@"bearer"][@"realm"]); + XCTAssertEqualObjects(@"invalid_token", challenges[@"bearer"][@"error"]); + XCTAssertEqualObjects(@"It's expired, really!", challenges[@"bearer"][@"error_description"]); + + challenges = [CMISMimeHelper challengesFromAuthenticateHeader:@"Newauth realm=\"apps\", type=1, title=\"Login to \\\"apps\\\"\", Basic realm=\"simple\""]; + XCTAssertNotNil(challenges); + XCTAssertEqualObjects(@"apps", challenges[@"newauth"][@"realm"]); + XCTAssertEqualObjects(@"1", challenges[@"newauth"][@"type"]); + XCTAssertEqualObjects(@"Login to \"apps\"", challenges[@"newauth"][@"title"]); + XCTAssertEqualObjects(@"simple", challenges[@"basic"][@"realm"]); + + challenges = [CMISMimeHelper challengesFromAuthenticateHeader:@"a1, a2 ,a3 ,a4"]; + XCTAssertNotNil(challenges); + XCTAssertTrue(challenges[@"a1"]); + XCTAssertTrue(challenges[@"a2"]); + XCTAssertTrue(challenges[@"a3"]); + XCTAssertTrue(challenges[@"a4"]); +} + @end
