chemistry-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From psut...@apache.org
Subject svn commit: r1795295 [4/5] - in /chemistry/objectivecmis/trunk: ObjectiveCMIS.xcodeproj/ ObjectiveCMIS/ ObjectiveCMIS/Bindings/ ObjectiveCMIS/Bindings/AtomPub/ ObjectiveCMIS/Bindings/Browser/ ObjectiveCMIS/Client/ ObjectiveCMIS/Common/ ObjectiveCMIS/Ut...
Date Tue, 16 May 2017 10:33:33 GMT
Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Client/CMISSession.m
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Client/CMISSession.m?rev=1795295&r1=1795294&r2=1795295&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Client/CMISSession.m (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Client/CMISSession.m Tue May 16 10:33:32 2017
@@ -31,6 +31,8 @@
 #import "CMISTypeDefinition.h"
 #import "CMISDefaultNetworkProvider.h"
 #import "CMISLog.h"
+#import "CMISChangeEvents.h"
+#import "CMISStringInOutParameter.h"
 
 @interface CMISSession ()
 @property (nonatomic, strong, readwrite) CMISObjectConverter *objectConverter;
@@ -703,4 +705,177 @@
     return request;
 }
 
+- (CMISRequest*)retrieveAclFromCMISObject:objectId
+                     onlyBasicPermissions:(BOOL)onlyBasicPermissions
+                          completionBlock:(void (^)(CMISAcl *acl, NSError *error))completionBlock
+{
+    if (objectId == nil) {
+        completionBlock(nil, [CMISErrors createCMISErrorWithCode:kCMISErrorCodeInvalidArgument detailedDescription:@"Must provide object id"]);
+        return nil;
+    }
+    
+    return [self.binding.aclService retrieveAcl:objectId onlyBasicPermissions:onlyBasicPermissions completionBlock:^(CMISAcl *acl, NSError *error) {
+        if (error) {
+            CMISLogError(@"Could not retrieve acl: %@", error.description);
+            if (completionBlock) {
+                completionBlock(nil, [CMISErrors cmisError:error cmisErrorCode:kCMISErrorCodeRuntime]);
+            }
+        } else {
+            if (completionBlock) {
+                completionBlock(acl, nil);
+            }
+        }
+    }];
+}
+
+- (CMISRequest*)applyAclToCMISObject:objectId
+                             addAces:(CMISAcl *)addAces
+                          removeAces:(CMISAcl *)removeAces
+                      aclPropagation:(CMISAclPropagation)aclPropagation
+                     completionBlock:(void (^)(CMISAcl *acl, NSError *error))completionBlock
+{
+    if (objectId == nil) {
+        completionBlock(nil, [CMISErrors createCMISErrorWithCode:kCMISErrorCodeInvalidArgument detailedDescription:@"Must provide object id"]);
+        return nil;
+    }
+    
+    return [self.binding.aclService applyAcl:objectId addAces:addAces removeAces:removeAces aclPropagation:aclPropagation completionBlock:^(CMISAcl *acl, NSError *error) {
+        if (error) {
+            CMISLogError(@"Could not apply acl: %@", error.description);
+            if (completionBlock) {
+                completionBlock(nil, [CMISErrors cmisError:error cmisErrorCode:kCMISErrorCodeRuntime]);
+            }
+        } else {
+            if (completionBlock) {
+                completionBlock(acl, nil);
+            }
+        }
+    }];
+}
+
+- (CMISRequest*)setAclOnCMISObject:objectId
+                              aces:(CMISAcl *)aces
+                   completionBlock:(void (^)(CMISAcl *acl, NSError *error))completionBlock
+{
+    if (objectId == nil) {
+        completionBlock(nil, [CMISErrors createCMISErrorWithCode:kCMISErrorCodeInvalidArgument detailedDescription:@"Must provide object id"]);
+        return nil;
+    }
+    
+    return [self.binding.aclService setAcl:objectId aces:aces completionBlock:^(CMISAcl *acl, NSError *error) {
+        if (error) {
+            CMISLogError(@"Could not set acl: %@", error.description);
+            if (completionBlock) {
+                completionBlock(nil, [CMISErrors cmisError:error cmisErrorCode:kCMISErrorCodeRuntime]);
+            }
+        } else {
+            if (completionBlock) {
+                completionBlock(acl, nil);
+            }
+        }
+    }];
+}
+
+- (CMISRequest*)retrieveContentChangesWithChangeLogToken:(NSString *)changeLogToken
+                                       includeProperties:(BOOL)includeProperties
+                                                maxItems:(NSNumber *)maxItems
+                                        operationContext:(CMISOperationContext *)operationContext
+                                         completionBlock:(void (^)(CMISChangeEvents *changeEvents, NSError *error))completionBlock
+{
+    CMISStringInOutParameter *changeLogTokenHolder = [CMISStringInOutParameter inOutParameterUsingInParameter:changeLogToken];
+    
+    return [self.binding.discoveryService retrieveContentChanges:changeLogTokenHolder
+                                               includeProperties:includeProperties
+                                                          filter:operationContext.filterString
+                                                includePolicyIds:operationContext.includePolicies
+                                                      includeAcl:operationContext.includeACLs
+                                                        maxItems:maxItems
+                                                 completionBlock:^(CMISObjectList *objectList, NSError *error) {
+                                                     if (error) {
+                                                         CMISLogError(@"Could not retrieve content changes: %@", error.description);
+                                                         completionBlock(nil, [CMISErrors cmisError:error cmisErrorCode:kCMISErrorCodeRuntime]);
+                                                     } else {
+                                                         CMISChangeEvents *changeEvents = [CMISObjectConverter convertChangeEvents:changeLogTokenHolder.outParameter objectList:objectList];
+                                                         completionBlock(changeEvents, nil);
+                                                     }
+                                                 }];
+}
+
+- (CMISRequest*)retrieveContentChangesWithChangeLogToken:(NSString *)changeLogToken
+                                       includeProperties:(BOOL)includeProperties
+                                        operationContext:(CMISOperationContext *)operationContext
+                                         completionBlock:(void (^)(CMISPagedResult *result, NSError *error))completionBlock
+{
+    CMISRequest *request = [[CMISRequest alloc] init];
+    
+    __block NSString *token = changeLogToken;
+    __block BOOL firstPage = YES;
+    CMISFetchNextPageBlock fetchNextPageBlock = ^(int skipCount, int maxItems, CMISFetchNextPageBlockCompletionBlock pageBlockCompletionBlock)
+    {
+        CMISStringInOutParameter *changeLogTokenHolder = [CMISStringInOutParameter inOutParameterUsingInParameter:token];
+        
+        // Fetch results through navigationService
+        CMISRequest * contentChangesRequest = [self.binding.discoveryService retrieveContentChanges:changeLogTokenHolder
+                                                                              includeProperties:includeProperties
+                                                                                         filter:operationContext.filterString
+                                                                               includePolicyIds:operationContext.includePolicies
+                                                                                     includeAcl:operationContext.includeACLs
+                                                                                       maxItems:@(maxItems)
+                                                                                completionBlock:^(CMISObjectList *objectList, NSError *error) {
+                                                                                    if (error) {
+                                                                                        pageBlockCompletionBlock(nil, [CMISErrors cmisError:error cmisErrorCode:kCMISErrorCodeConnection]);
+                                                                                    } else {
+                                                                                        CMISFetchNextPageBlockResult *result = [[CMISFetchNextPageBlockResult alloc] init];
+                                                                                        
+                                                                                        NSMutableArray *page = [[NSMutableArray alloc] initWithCapacity:objectList.objects.count];
+                                                                                        for (CMISObjectData *objectData in objectList.objects) {
+                                                                                            CMISChangeEvent *changeEvent = [CMISObjectConverter convertChangeEvent:objectData];
+                                                                                            [page addObject:changeEvent];
+                                                                                        }
+                                                                                        
+                                                                                        if (!firstPage) {
+                                                                                            // the last entry of the previous page is repeated
+                                                                                            // -> remove the first entry
+                                                                                            if (page.count > 0) {
+                                                                                                [page removeObjectAtIndex:0];
+                                                                                            }
+                                                                                        }
+                                                                                        
+                                                                                        firstPage = NO;
+                                                                                        
+                                                                                        if (changeLogTokenHolder.outParameter) {
+                                                                                            // the browser binding returns a new token
+                                                                                            token = changeLogTokenHolder.outParameter;
+                                                                                        } else {
+                                                                                            // the atompub binding does not return a new token,
+                                                                                            // but might return a link to the next Atom feed
+                                                                                            token = nil;
+                                                                                            // TODO handle nextLink in case of atompub binding
+                                                                                        }
+                                                                                        
+                                                                                        result.hasMoreItems = objectList.hasMoreItems;
+                                                                                        result.numItems = objectList.numItems;
+                                                                                        
+                                                                                        result.resultArray = page;
+                                                                                        pageBlockCompletionBlock(result, error);
+                                                                                    }
+                                                                                }];
+        
+        // set the underlying request object on the object returned to the original caller
+        request.httpRequest = contentChangesRequest.httpRequest;
+    };
+    
+    [CMISPagedResult pagedResultUsingFetchBlock:fetchNextPageBlock
+                                limitToMaxItems:operationContext.maxItemsPerPage
+                             startFromSkipCount:operationContext.skipCount
+                                completionBlock:^(CMISPagedResult *result, NSError *error) {
+                                    if (error) {
+                                        completionBlock(nil, [CMISErrors cmisError:error cmisErrorCode:kCMISErrorCodeRuntime]);
+                                    } else {
+                                        completionBlock(result, nil);
+                                    }
+                                }];
+    return request;
+}
+
 @end

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISAuthenticationProvider.h
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISAuthenticationProvider.h?rev=1795295&r1=1795294&r2=1795295&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISAuthenticationProvider.h (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISAuthenticationProvider.h Tue May 16 10:33:32 2017
@@ -19,13 +19,16 @@
 
 #import <Foundation/Foundation.h>
 
+@class CMISBindingSession;
+
 @protocol CMISAuthenticationProvider <NSObject>
 
 /**
 * Returns a set of HTTP headers (key-value pairs) that should be added to a
 * HTTP call. This will be called by the AtomPub and the Web Services
 * binding. You might want to check the binding in use before you set the
-* headers.
+* headers. This property can be overwritten by the asyncHttpHeadersToApply:
+* method - if implemented.
 *
 * @return the HTTP headers or nil if no additional headers should be set
 */
@@ -37,24 +40,22 @@
 - (void)updateWithHttpURLResponse:(NSHTTPURLResponse*)httpUrlResponse;
 
 /**
- * checks if provider can authenticate against provided protection space
+ * callback when authentication challenge was received using NSURLSession
  */
-- (BOOL)canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace;
+- (void)didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
+          completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler;
 
-/**
- * callback when authentication challenge was cancelled
- */
-- (void)didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
+@optional
 
 /**
- * callback when authentication challenge was received
+ * Called when the CMISBindingSession gets initialized. Use a weak reference to avoid 
+ * reference cycles when storing the session in a property.
  */
-- (void)didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
+- (void)setSession:(CMISBindingSession *)session;
 
 /**
- * callback when authentication challenge was received using NSURLSession
+ * If this method is implemented the property httpHeadersToApply is overwritten
  */
-- (void)didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
-          completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler;
+- (void)asyncHttpHeadersToApply:(void(^)(NSDictionary *headers, NSError *cmisError))completionBlock;
 
 @end

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISConstants.h
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISConstants.h?rev=1795295&r1=1795294&r2=1795295&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISConstants.h (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISConstants.h Tue May 16 10:33:32 2017
@@ -101,6 +101,7 @@ extern NSString * const kCMISRepositoryC
 
 // URL parameters
 extern NSString * const kCMISParameterChangeToken;
+extern NSString * const kCMISParameterChangeLogToken;
 extern NSString * const kCMISParameterOverwriteFlag;
 extern NSString * const kCMISParameterIncludeAllowableActions;
 extern NSString * const kCMISParameterFilter;
@@ -111,6 +112,7 @@ extern NSString * const kCMISParameterOr
 extern NSString * const kCMISParameterIncludePathSegment;
 extern NSString * const kCMISParameterIncludeRelationships;
 extern NSString * const kCMISParameterIncludePolicyIds;
+extern NSString * const kCMISParameterIncludeProperties;
 extern NSString * const kCMISParameterIncludeAcl;
 extern NSString * const kCMISParameterRenditionFilter;
 extern NSString * const kCMISParameterSkipCount;
@@ -129,6 +131,8 @@ extern NSString * const kCMISParameterRe
 extern NSString * const kCMISParameterTypeId;
 extern NSString * const kCMISParameterStatement;
 extern NSString * const kCMISParameterSearchAllVersions;
+extern NSString * const kCMISParameterOnlyBasicPermissions;
+extern NSString * const kCMISParameterAclPropagation;
 
 // Parameter Values
 extern NSString * const kCMISParameterValueTrue;
@@ -145,6 +149,12 @@ extern NSString * const kCMISContentStre
 extern NSString * const kCMISContentStreamAllowedValueAllowed;
 extern NSString * const kCMISContentStreamAllowedValueNotAllowed;
 
+// ChangeType enum values
+extern NSString * const kCMISChangeTypeCreated;
+extern NSString * const kCMISChangeTypeUpdated;
+extern NSString * const kCMISChangeTypeDeleted;
+extern NSString * const kCMISChangeTypeSecurity;
+
 // Background network default values
 extern NSString * const kCMISDefaultBackgroundNetworkSessionId;
 extern NSString * const kCMISDefaultBackgroundNetworkSessionSharedContainerId;

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISConstants.m
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISConstants.m?rev=1795295&r1=1795294&r2=1795295&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISConstants.m (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISConstants.m Tue May 16 10:33:32 2017
@@ -112,6 +112,7 @@ NSString * const kCMISRepositoryCapabili
 
 // Parameters
 NSString * const kCMISParameterChangeToken = @"changeToken";
+NSString * const kCMISParameterChangeLogToken = @"changeLogToken";
 NSString * const kCMISParameterOverwriteFlag = @"overwriteFlag";
 NSString * const kCMISParameterIncludeAllowableActions = @"includeAllowableActions";
 NSString * const kCMISParameterFilter = @"filter";
@@ -122,6 +123,7 @@ NSString * const kCMISParameterOrderBy =
 NSString * const kCMISParameterIncludePathSegment = @"includePathSegment";
 NSString * const kCMISParameterIncludeRelationships = @"includeRelationships";
 NSString * const kCMISParameterIncludePolicyIds = @"includePolicyIds";
+NSString * const kCMISParameterIncludeProperties = @"includeProperties";
 NSString * const kCMISParameterIncludeAcl = @"includeACL";
 NSString * const kCMISParameterRenditionFilter = @"renditionFilter";
 NSString * const kCMISParameterSkipCount = @"skipCount";
@@ -140,6 +142,8 @@ NSString * const kCMISParameterReturnVer
 NSString * const kCMISParameterTypeId = @"typeId";
 NSString * const kCMISParameterStatement = @"statement";
 NSString * const kCMISParameterSearchAllVersions = @"searchAllVersions";
+NSString * const kCMISParameterOnlyBasicPermissions = @"onlyBasicPermissions";
+NSString * const kCMISParameterAclPropagation = @"ACLPropagation";
 
 // Parameter Values
 NSString * const kCMISParameterValueTrue = @"true";
@@ -156,6 +160,12 @@ NSString * const kCMISContentStreamAllow
 NSString * const kCMISContentStreamAllowedValueAllowed = @"allowed";
 NSString * const kCMISContentStreamAllowedValueNotAllowed = @"notallowed";
 
+// ChangeType enum values
+NSString * const kCMISChangeTypeCreated = @"created";
+NSString * const kCMISChangeTypeUpdated = @"updated";
+NSString * const kCMISChangeTypeDeleted = @"deleted";
+NSString * const kCMISChangeTypeSecurity = @"security";
+
 // Background network default values
 NSString * const kCMISDefaultBackgroundNetworkSessionId = @"ObjectiveCMIS";
 NSString * const kCMISDefaultBackgroundNetworkSessionSharedContainerId = @"ObjectiveCMISContainer";

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISEnums.h
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISEnums.h?rev=1795295&r1=1795294&r2=1795295&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISEnums.h (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISEnums.h Tue May 16 10:33:32 2017
@@ -126,10 +126,9 @@ typedef NS_ENUM(NSInteger, CMISExtension
     CMISExtensionLevelObject,
     CMISExtensionLevelProperties,
     CMISExtensionLevelAllowableActions,
-    CMISExtensionLevelAcl
-    // TODO expose the remaining extensions as they are implemented
-    // CMISExtensionLevelPolicies, CMISExtensionLevelChangeEvent
-
+    CMISExtensionLevelAcl,
+    CMISExtensionLevelPolicies,
+    CMISExtensionLevelChangeEvent
 };
 
 // UnfileObject
@@ -207,7 +206,15 @@ typedef NS_ENUM(NSInteger, CMISCapabilit
     CMISCapabilityOrderByCustom
 };
 
-// ReturnVersion
+// ACL Propagation
+typedef NS_ENUM(NSInteger, CMISAclPropagation)
+{
+    CMISAclPropagationRepositoryDetermined,
+    CMISAclPropagationObjectOnly,
+    CMISAclPropagationPropagate
+};
+
+// Return Version
 typedef NS_ENUM(NSInteger, CMISReturnVersion)
 {
     NOT_PROVIDED,
@@ -216,13 +223,25 @@ typedef NS_ENUM(NSInteger, CMISReturnVer
     LATEST_MAJOR
 };
 
+// Change Type
+typedef NS_ENUM(NSInteger, CMISChangeType)
+{
+    CMISChangeTypeUnknown,
+    CMISChangeTypeCreated,
+    CMISChangeTypeUpdated,
+    CMISChangeTypeDeleted,
+    CMISChangeTypeSecurity
+};
+
 @interface CMISEnums : NSObject 
 
 + (NSString *)stringForIncludeRelationShip:(CMISIncludeRelationship)includeRelationship;
 + (NSString *)stringForUnfileObject:(CMISUnfileObject)unfileObject;
 + (NSString *)stringForReturnVersion:(BOOL)major;
++ (NSString *)stringForAclPropagation:(CMISAclPropagation)aclPropagation;;
 + (CMISBaseType)enumForBaseId:(NSString *)baseId;
 + (CMISContentStreamAllowedType)enumForContentStreamAllowed:(NSString *)contentStreamAllowed;
 + (CMISPropertyType)enumForPropertyType:(NSString *)typeString;
++ (CMISChangeType)enumForChangeType:(NSString *)changeType;
 
 @end

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISEnums.m
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISEnums.m?rev=1795295&r1=1795294&r2=1795295&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISEnums.m (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISEnums.m Tue May 16 10:33:32 2017
@@ -67,6 +67,26 @@
     return unfileObjectString;
 }
 
++ (NSString *)stringForAclPropagation:(CMISAclPropagation)aclPropagation;
+{
+    NSString *aclPropagationString = nil;
+    switch (aclPropagation) {
+        case CMISAclPropagationRepositoryDetermined:
+            aclPropagationString = @"repositorydetermined";
+            break;
+        case  CMISAclPropagationObjectOnly:
+            aclPropagationString = @"objectonly";
+            break;
+        case CMISAclPropagationPropagate:
+            aclPropagationString = @"propagate";
+            break;
+        default:
+            CMISLogError(@"Inavlid enum type %d", (int)aclPropagation);
+            break;
+    }
+    return aclPropagationString;
+}
+
 + (NSString *)stringForReturnVersion:(BOOL)major
 {
     return major == NO ? kCMISParameterValueReturnValueLatest : kCMISParameterValueReturnValueLatestMajor;
@@ -129,4 +149,19 @@
     return propertyType;
 }
 
-@end
\ No newline at end of file
++ (CMISChangeType)enumForChangeType:(NSString *)changeType
+{
+    if([kCMISChangeTypeCreated isEqualToString:changeType]) {
+        return CMISChangeTypeCreated;
+    } else if([kCMISChangeTypeUpdated isEqualToString:changeType]) {
+        return CMISChangeTypeUpdated;
+    } else if([kCMISChangeTypeDeleted isEqualToString:changeType]) {
+        return CMISChangeTypeDeleted;
+    } else if([kCMISChangeTypeSecurity isEqualToString:changeType]) {
+        return CMISChangeTypeSecurity;
+    } else {
+        return CMISChangeTypeUnknown;
+    }
+}
+
+@end

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISErrors.h
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISErrors.h?rev=1795295&r1=1795294&r2=1795295&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISErrors.h (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISErrors.h Tue May 16 10:33:32 2017
@@ -99,6 +99,21 @@ extern NSString * const kCMISErrorDescri
 extern NSString * const kCMISErrorDescriptionStreamNotSupported;
 extern NSString * const kCMISErrorDescriptionUpdateConflict;
 extern NSString * const kCMISErrorDescriptionVersioning;
+//OAuth specific - to be used in the userInfo dictionary when OAuth token request fails
+extern NSString * const kCMISErrorOAuthExceptionErrorKey;
+extern NSString * const kCMISErrorOAuthExceptionDescriptionKey;
+extern NSString * const kCMISErrorOAuthExceptionUriKey;
+//OAuth specific error codes as defined in RFC 6749 5.2
+extern NSString * const kCMISErrorOAuthCodeInvalidRequest;
+extern NSString * const kCMISErrorOAuthCodeInvalidClient;
+extern NSString * const kCMISErrorOAuthCodeInvalidGrant;
+extern NSString * const kCMISErrorOAuthCodeUnauthorizedClient;
+extern NSString * const kCMISErrorOAuthCodeUnsupportedGrantType;
+extern NSString * const kCMISErrorOAuthCodeInvalidScope;
+//Bearer OAuth specific error codes as defined in RFC 6750 6.2
+extern NSString * const kCMISErrorBearerOAuthCodeInvalidRequest;
+extern NSString * const kCMISErrorBearerOAuthCodeInvalidToken; // ask the user to authenticate again
+extern NSString * const kCMISErrorBearerOAuthCodeInsufficientScope;
 
 /** This class defines Errors in the Objective-C CMIS library
  
@@ -128,5 +143,15 @@ extern NSString * const kCMISErrorDescri
  @return the CMIS error as NSError object with error domain org.apache.chemistry.objectivecmis
  */
 + (NSError *)createCMISErrorWithCode:(CMISErrorCodes)code detailedDescription:(NSString *)detailedDescription;
+/** Creates a new CMIS error
+ 
+ This is the direct way of creating CMIS errors
+ 
+ @param code the CMIS Error code to be used
+ @param detailedDescription a detailed description to be added to the localizedDescription. Use nil if none is available/needed.
+ @param additionalUserInfo additional user info dictionary to be added to the NSError's userInfo
+ @return the CMIS error as NSError object with error domain org.apache.chemistry.objectivecmis
+ */
++ (NSError *)createCMISErrorWithCode:(CMISErrorCodes)code detailedDescription:(NSString *)detailedDescription additionalUserInfo:(NSDictionary *)additionalUserInfo;
 @end
 

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISErrors.m
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISErrors.m?rev=1795295&r1=1795294&r2=1795295&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISErrors.m (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISErrors.m Tue May 16 10:33:32 2017
@@ -26,8 +26,8 @@ NSString * const kCMISErrorDomainName =
 /**
  Note, the string definitions below should not be used by themselves. Rather, they should be used to 
  obtain the localized string. Therefore, the proper use in the code would be e.g.
- NSLocalizedString(kCMISNoReturnErrorDescription,kCMISNoReturnErrorDescription)
- (the second parameter in NSLocalizedString is a Comment and may be set to nil)
+ LocalizedString(kCMISNoReturnErrorDescription,kCMISNoReturnErrorDescription)
+ (the second parameter in LocalizedString is a Comment and may be set to nil)
  */
 //Basic Errors
 
@@ -58,6 +58,26 @@ NSString * const kCMISErrorDescriptionSt
 NSString * const kCMISErrorDescriptionUpdateConflict = @"Update Conflict Error";
 NSString * const kCMISErrorDescriptionVersioning = @"Versioning Error";
 
+//OAuth specific - to be used in the userInfo dictionary when OAuth token request fails
+NSString * const kCMISErrorOAuthExceptionErrorKey = @"CMISErrorOAuthExceptionErrorKey";
+NSString * const kCMISErrorOAuthExceptionDescriptionKey = @"CMISErrorOAuthExceptionDescriptionKey";
+NSString * const kCMISErrorOAuthExceptionUriKey = @"CMISErrorOAuthExceptionUriKey";
+
+//OAuth specific error codes as defined in RFC 6749 5.2
+NSString * const kCMISErrorOAuthCodeInvalidRequest = @"invalid_request";
+NSString * const kCMISErrorOAuthCodeInvalidClient = @"invalid_client";
+NSString * const kCMISErrorOAuthCodeInvalidGrant = @"invalid_grant"; // ask the user to authenticate again
+NSString * const kCMISErrorOAuthCodeUnauthorizedClient = @"unauthorized_client";
+NSString * const kCMISErrorOAuthCodeUnsupportedGrantType = @"unsupported_grant_type";
+NSString * const kCMISErrorOAuthCodeInvalidScope = @"invalid_scope";
+
+//Bearer OAuth specific error codes as defined in RFC 6750 6.2
+NSString * const kCMISErrorBearerOAuthCodeInvalidRequest = @"invalid_request";
+NSString * const kCMISErrorBearerOAuthCodeInvalidToken = @"invalid_token"; // ask the user to authenticate again
+NSString * const kCMISErrorBearerOAuthCodeInsufficientScope = @"insufficient_scope";
+
+
+
 @interface CMISErrors ()
 + (NSString *)localizedDescriptionForCode:(CMISErrorCodes)code;
 @end
@@ -82,9 +102,20 @@ NSString * const kCMISErrorDescriptionVe
 
 + (NSError *)createCMISErrorWithCode:(CMISErrorCodes)code detailedDescription:(NSString *)detailedDescription
 {
+    return [CMISErrors createCMISErrorWithCode:code detailedDescription:detailedDescription additionalUserInfo:nil];
+}
+
++ (NSError *)createCMISErrorWithCode:(CMISErrorCodes)code detailedDescription:(NSString *)detailedDescription additionalUserInfo:(NSDictionary *)additionalUserInfo
+{
     NSDictionary *userInfo = [CMISDictionaryUtil userInfoDictionaryForErrorWithDescription:[CMISErrors localizedDescriptionForCode:code]
                                                                                     reason:detailedDescription
                                                                            underlyingError:nil];
+    if (additionalUserInfo) {
+        NSMutableDictionary *mutableUserInfo = [userInfo mutableCopy];
+        [mutableUserInfo addEntriesFromDictionary:additionalUserInfo];
+        userInfo = [mutableUserInfo copy];
+    }
+    
     return [NSError errorWithDomain:kCMISErrorDomainName code:code userInfo:userInfo];
 }
 

Added: chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISOAuthAuthenticationProvider.h
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISOAuthAuthenticationProvider.h?rev=1795295&view=auto
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISOAuthAuthenticationProvider.h (added)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISOAuthAuthenticationProvider.h Tue May 16 10:33:32 2017
@@ -0,0 +1,44 @@
+/*
+  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 "CMISStandardAuthenticationProvider.h"
+
+@class CMISOAuthAuthenticationProvider, CMISOAuthToken;
+
+@protocol CMISOAuthAuthenticationProviderDelegate <NSObject>
+
+- (void)cmisOAuthAuthenticationProvider:(CMISOAuthAuthenticationProvider *)authenticationProvider didUpdateToken:(CMISOAuthToken *)token withError:(NSError *)error;
+
+@end
+
+/**
+ * OAuth 2.0 Authentication Provider.
+ * This authentication provider implements OAuth 2.0 (RFC 6749) Bearer Tokens
+ * (RFC 6750).
+ * The provider can be either configured with an authorization code or with an
+ * existing bearer token. Token endpoint and client ID are always required. If a
+ * client secret is required depends on the authorization server.
+ */
+@interface CMISOAuthAuthenticationProvider : CMISStandardAuthenticationProvider
+
+@property (nonatomic, strong, readonly) CMISOAuthToken *token;
+
+@property (nonatomic, weak) id<CMISOAuthAuthenticationProviderDelegate> delegate;
+
+@end

Added: chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISOAuthAuthenticationProvider.m
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISOAuthAuthenticationProvider.m?rev=1795295&view=auto
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISOAuthAuthenticationProvider.m (added)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISOAuthAuthenticationProvider.m Tue May 16 10:33:32 2017
@@ -0,0 +1,410 @@
+/*
+  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 "CMISOAuthAuthenticationProvider.h"
+#import "CMISOAuthToken.h"
+#import "CMISLog.h"
+#import "CMISBindingSession.h"
+#import "CMISErrors.h"
+#import "CMISHttpResponse.h"
+#import "CMISHttpRequest.h"
+#import "CMISURLUtil.h"
+#import "CMISDictionaryUtil.h"
+#import "CMISDefaultNetworkProvider.h"
+#import "CMISOAuthHttpRequest.h"
+#import "CMISOAuthHttpResponse.h"
+
+@interface CMISOAuthAuthenticationProvider ()
+
+@property (nonatomic, strong, readwrite) CMISOAuthToken *token;
+@property (nonatomic, strong) NSNumber *defaultTokenLifetime;
+
+@property (nonatomic, weak) CMISBindingSession *session;
+
+@property (nonatomic, strong) NSOperationQueue *operationQueue;
+
+@end
+
+@interface CMISOAuthFetchOperation : NSOperation {
+    BOOL executing;
+    BOOL finished;
+}
+
+@property (nonatomic, weak) CMISOAuthAuthenticationProvider *authProvider;
+@property (nonatomic, weak) NSThread *originalThread;
+@property (nonatomic, copy) void (^operationCompletionBlock)(NSString *accessToken, NSError *error);
+
+- (instancetype)initWithOAuthProvider:(CMISOAuthAuthenticationProvider *)authProvider completionBlock:(void(^)(NSString *accessToken, NSError *error))completionBlock;
+- (void)completeOperation;
+
+@end
+
+@implementation CMISOAuthAuthenticationProvider
+
+- (instancetype)init
+{
+    self = [super init];
+    if (self) {
+        self.defaultTokenLifetime = [NSNumber numberWithInt:3600];
+        
+        self.operationQueue = [[NSOperationQueue alloc] init];
+        /* We need a serial queue as we need to make sure that only one thread at a time requests or refreshes the token
+         * We cannot use a NSLock object to synchronize or else we would run into a deadlock because of the asynchronous call to the server */
+        [self.operationQueue setMaxConcurrentOperationCount:1];
+        if ([self.operationQueue respondsToSelector:@selector(setQualityOfService:)]) {
+            // Supported since iOS8
+            [self.operationQueue setQualityOfService:NSQualityOfServiceBackground];
+        }
+    }
+    return self;
+}
+
+- (void)setSession:(CMISBindingSession *)session
+{
+    _session = session;
+    
+    if (self.token == nil) {
+        // get predefined access token
+        NSString *accessToken = nil;
+        if ([[self.session objectForKey:kCMISSessionParameterOAuthAccessToken] isKindOfClass:NSString.class]) {
+            accessToken = (NSString *)[self.session objectForKey:kCMISSessionParameterOAuthAccessToken];
+        }
+        
+        // get predefined refresh token
+        NSString *refreshToken = nil;
+        if ([[self.session objectForKey:kCMISSessionParameterOAuthRefreshToken] isKindOfClass:NSString.class]) {
+            refreshToken = (NSString *)[self.session objectForKey:kCMISSessionParameterOAuthRefreshToken];
+        }
+        
+        // get predefined expiration timestamp
+        NSNumber *expirationTimestamp = [NSNumber numberWithInt:0];
+        if ([[self.session objectForKey:kCMISSessionParameterOAuthExpirationTimestamp] isKindOfClass:NSString.class]) {
+            expirationTimestamp = [NSNumber numberWithLongLong:[[self.session objectForKey:kCMISSessionParameterOAuthExpirationTimestamp] longLongValue]];
+        } else if ([[self.session objectForKey:kCMISSessionParameterOAuthExpirationTimestamp] isKindOfClass:NSNumber.class]) {
+            expirationTimestamp = (NSNumber *)[self.session objectForKey:kCMISSessionParameterOAuthExpirationTimestamp];
+        }
+        
+        // get default token lifetime
+        if ([[self.session objectForKey:kCMISSessionParameterOAuthDefaultTokenLifetime] isKindOfClass:NSString.class]) {
+            self.defaultTokenLifetime = [NSNumber numberWithLongLong:[[self.session objectForKey:kCMISSessionParameterOAuthDefaultTokenLifetime] longLongValue]];
+        } else if ([[self.session objectForKey:kCMISSessionParameterOAuthDefaultTokenLifetime] isKindOfClass:NSNumber.class]) {
+            self.defaultTokenLifetime = (NSNumber *)[self.session objectForKey:kCMISSessionParameterOAuthDefaultTokenLifetime];
+        }
+        
+        self.token = [[CMISOAuthToken alloc] initWithAccessToken:accessToken refreshToken:refreshToken expirationTimestamp:expirationTimestamp];
+    }
+}
+
+- (void)asyncHttpHeadersToApply:(void(^)(NSDictionary *headers, NSError *cmisError))completionBlock
+{
+    CMISOAuthFetchOperation *oAuthFetchOperation = [[CMISOAuthFetchOperation alloc] initWithOAuthProvider:self completionBlock:^(NSString *accessToken, NSError *error) {
+        if (error) {
+            completionBlock(nil, error);
+        } else {
+            NSMutableDictionary *headers = [[super httpHeadersToApply] mutableCopy]; //TODO check if httpHeadersToApply should be called
+            if(!headers) {
+                headers = [NSMutableDictionary new];
+            }
+            
+            [headers setObject:[NSSet setWithObject:[NSString stringWithFormat:@"Bearer %@", accessToken]] forKey:@"Authorization"];
+            
+            completionBlock(headers, nil);
+        }
+    }];
+    
+    [self.operationQueue addOperation:oAuthFetchOperation];
+}
+
+
+@end
+
+@implementation CMISOAuthFetchOperation
+
+- (instancetype)initWithOAuthProvider:(CMISOAuthAuthenticationProvider *)authProvider completionBlock:(void (^)(NSString *, NSError *))completionBlock
+{
+    self = [super init];
+    if (self) {
+        self.authProvider = authProvider;
+        self.operationCompletionBlock = completionBlock;
+        self.originalThread = [NSThread currentThread];
+    }
+    return self;
+}
+
+- (void)main
+{
+    // do not invoke super!
+    
+    if (self.isCancelled) {
+        NSError *error = [CMISErrors createCMISErrorWithCode:kCMISErrorCodeCancelled detailedDescription:@"Could not get token as CMISOAuthFetchOperation was cancelled"];
+        [self performSelector:@selector(executeCompletionBlockError:) onThread:self.originalThread withObject:error waitUntilDone:NO];
+    } else {
+        [self accessTokenWithCompletionBlock:^(NSError *error) {
+            // call the completion block on the original thread
+            if (self.originalThread) {
+                if (error) {
+                    [self performSelector:@selector(executeCompletionBlockError:) onThread:self.originalThread withObject:error waitUntilDone:NO];
+                } else {
+                    [self performSelector:@selector(executeCompletionBlockAccessToken:) onThread:self.originalThread withObject:self.authProvider.token.accessToken waitUntilDone:NO];
+                }
+            }
+        }];
+    }
+}
+
+- (void)accessTokenWithCompletionBlock:(void(^)(NSError *error))completionBlock
+{
+    void (^continueWithUpdatedToken)(NSError*) = ^(NSError *error) {
+        
+        [self.authProvider.delegate cmisOAuthAuthenticationProvider:self.authProvider didUpdateToken:self.authProvider.token withError:error];
+        
+        completionBlock(error);
+    };
+    
+    if (!self.authProvider.token.accessToken) {
+        if (!self.authProvider.token.refreshToken) {
+            [self requestToken:continueWithUpdatedToken];
+        } else {
+            [self refreshToken:continueWithUpdatedToken];
+        }
+    } else if ([self.authProvider.token isExpired]) {
+        [self refreshToken:continueWithUpdatedToken];
+    } else { // we already have a valid token
+        completionBlock(nil);
+    }
+}
+
+- (void)requestToken:(void(^)(NSError *error))completionBlock
+{
+    if ([CMISLog sharedInstance].logLevel == CMISLogLevelDebug) {
+        CMISLogDebug(@"Requesting new OAuth access token.");
+    }
+    
+    [self makeRequestIsRefresh:NO completionBlock:^(NSError *error) {
+        if ([CMISLog sharedInstance].logLevel == CMISLogLevelTrace) {
+            CMISLogTrace(@"%@", [self.authProvider.token description]);
+        }
+        
+        completionBlock(error);
+    }];
+}
+
+- (void)refreshToken:(void(^)(NSError *error))completionBlock
+{
+    if ([CMISLog sharedInstance].logLevel == CMISLogLevelDebug) {
+        CMISLogDebug(@"Refreshing OAuth access token.");
+    }
+    
+    [self makeRequestIsRefresh:YES completionBlock:^(NSError *error) {
+        if ([CMISLog sharedInstance].logLevel == CMISLogLevelTrace) {
+            CMISLogTrace(@"%@", [self.authProvider.token description]);
+        }
+        
+        completionBlock(error);
+    }];
+}
+
+- (void)makeRequestIsRefresh:(BOOL)isRefresh completionBlock:(void(^)(NSError *error))completionBlock
+{
+    id tokenEndpoint = [self.authProvider.session objectForKey:kCMISSessionParameterOAuthTokenEndpoint];
+    if (![tokenEndpoint isKindOfClass:NSString.class]) {
+        completionBlock([CMISErrors createCMISErrorWithCode:kCMISErrorCodeConnection detailedDescription:@"Token endpoint not set!"]);
+    }
+    
+    if (isRefresh && !self.authProvider.token.refreshToken) {
+        completionBlock([CMISErrors createCMISErrorWithCode:kCMISErrorCodeConnection detailedDescription:@"No refresh token!"]);
+    }
+    
+    NSDictionary *headers = @{@"Content-Type" : @"application/x-www-form-urlencoded; charset=UTF-8"};
+    
+    NSMutableData *requestBody = [NSMutableData new];
+    // compile request
+    if (isRefresh) {
+        [self appendString:@"grant_type=refresh_token" toData:requestBody];
+        
+        [self appendString:@"&refresh_token=" toData:requestBody];
+        [self appendString:[CMISURLUtil encodeUrlParameterValue:self.authProvider.token.refreshToken] toData:requestBody];
+    } else {
+        [self appendString:@"grant_type=authorization_code" toData:requestBody];
+        
+        id code = [self.authProvider.session objectForKey:kCMISSessionParameterOAuthCode];
+        if (code) {
+            [self appendString:@"&code=" toData:requestBody];
+            [self appendString:[CMISURLUtil encodeUrlParameterValue:[code description]] toData:requestBody];
+        }
+        
+        id redirectUri = [self.authProvider.session objectForKey:kCMISSessionParameterOAuthRedirectUri];
+        if (redirectUri) {
+            [self appendString:@"&redirect_uri=" toData:requestBody];
+            [self appendString:[CMISURLUtil encodeUrlParameterValue:[redirectUri description]] toData:requestBody];
+        }
+    }
+    
+    id clientId = [self.authProvider.session objectForKey:kCMISSessionParameterOAuthClientId];
+    if (clientId) {
+        [self appendString:@"&client_id=" toData:requestBody];
+        [self appendString:[CMISURLUtil encodeUrlParameterValue:[clientId description]] toData:requestBody];
+    }
+    
+    id clientSecret = [self.authProvider.session objectForKey:kCMISSessionParameterOAuthClientSecret];
+    if (clientSecret) {
+        [self appendString:@"&client_secret=" toData:requestBody];
+        [self appendString:[CMISURLUtil encodeUrlParameterValue:[clientSecret description]] toData:requestBody];
+    }
+    
+    if ([CMISLog sharedInstance].logLevel == CMISLogLevelTrace) {
+        CMISLogTrace(@"Request body: %@", [[NSString alloc] initWithData:requestBody encoding:NSUTF8StringEncoding]);
+    }
+    
+    // request token
+    __weak typeof(self) weakSelf = self;
+    [self invoke:[NSURL URLWithString:(NSString *)tokenEndpoint]
+      httpMethod:HTTP_POST
+         session:self.authProvider.session
+            body:requestBody
+         headers:headers
+     cmisRequest:nil
+ completionBlock:^(CMISHttpResponse *httpResponse, NSError *error) {
+     NSError *cmisError = nil;
+     if (error) {
+         completionBlock(error);
+     } else {
+         // parse response
+         NSDictionary *jsonDictionary = [CMISOAuthHttpResponse parseResponse:httpResponse error:&cmisError];
+         
+         if(cmisError) { // there was an error parsing the response
+             completionBlock(cmisError);
+         } else {
+             id tokenType = [jsonDictionary cmis_objectForKeyNotNull:@"token_type"];
+             if (![tokenType isKindOfClass:NSString.class] || [@"bearer" caseInsensitiveCompare:(NSString *) tokenType] != NSOrderedSame) {
+                 cmisError = [CMISErrors createCMISErrorWithCode:kCMISErrorCodeConnection detailedDescription:[NSString stringWithFormat:@"Unsupported OAuth token type: %@", tokenType]];
+                 completionBlock(cmisError);
+                 return;
+             }
+             
+             id jsonAccessToken = [jsonDictionary cmis_objectForKeyNotNull:@"access_token"];
+             if (![jsonAccessToken isKindOfClass:NSString.class]) {
+                 cmisError = [CMISErrors createCMISErrorWithCode:kCMISErrorCodeConnection detailedDescription:@"Invalid OAuth access_token!"];
+                 completionBlock(cmisError);
+                 return;
+                 
+             }
+             
+             id jsonRefreshToken = [jsonDictionary cmis_objectForKeyNotNull:@"refresh_token"];
+             if (jsonRefreshToken && ![jsonRefreshToken isKindOfClass:NSString.class]) {
+                 cmisError = [CMISErrors createCMISErrorWithCode:kCMISErrorCodeConnection detailedDescription:@"Invalid OAuth refresh_token!"];
+                 completionBlock(cmisError);
+                 return;
+             }
+             
+             NSNumber *expiresIn = weakSelf.authProvider.defaultTokenLifetime;
+             id jsonExpiresIn = [jsonDictionary cmis_objectForKeyNotNull:@"expires_in"];
+             if (jsonExpiresIn) {
+                 if ([jsonExpiresIn isKindOfClass:NSNumber.class]) {
+                     expiresIn = (NSNumber *)jsonExpiresIn;
+                 } else if ([jsonExpiresIn isKindOfClass:NSString.class]) {
+                     expiresIn = [NSNumber numberWithLongLong:[(NSString *)jsonExpiresIn longLongValue]];
+                 } else {
+                     cmisError = [CMISErrors createCMISErrorWithCode:kCMISErrorCodeConnection detailedDescription:@"Invalid OAuth expires_in value!"];
+                     completionBlock(cmisError);
+                     return;
+                 }
+             }
+             
+             if ([expiresIn longLongValue] <= 0) {
+                 expiresIn = self.authProvider.defaultTokenLifetime;
+             }
+             
+             weakSelf.authProvider.token = [[CMISOAuthToken alloc] initWithAccessToken:jsonAccessToken refreshToken:jsonRefreshToken expirationTimestamp:[NSNumber numberWithLongLong:([expiresIn longLongValue] * 1000 + [[NSDate date] timeIntervalSince1970] * 1000)]]; // seconds to milliseconds
+             completionBlock(nil); // success
+         }
+     }
+ }];
+}
+
+- (void)invoke:(NSURL *)url httpMethod:(CMISHttpRequestMethod)httpRequestMethod session:(CMISBindingSession *)session body:(NSData *)body headers:(NSDictionary *)headers cmisRequest:(CMISRequest *)cmisRequest completionBlock:(void (^)(CMISHttpResponse *, NSError *))completionBlock
+{
+    NSMutableURLRequest *urlRequest = [CMISDefaultNetworkProvider createRequestForUrl:url
+                                                                           httpMethod:httpRequestMethod
+                                                                              session:session];
+    if (!cmisRequest.isCancelled) {
+        CMISHttpRequest* request = [CMISOAuthHttpRequest startRequest:urlRequest
+                                                           httpMethod:httpRequestMethod
+                                                          requestBody:body
+                                                              headers:headers
+                                                              session:session
+                                                      completionBlock:completionBlock];
+        if (request) {
+            cmisRequest.httpRequest = request;
+        }
+    } else {
+        if (completionBlock) {
+            completionBlock(nil, [CMISErrors createCMISErrorWithCode:kCMISErrorCodeCancelled
+                                                 detailedDescription:@"Request was cancelled"]);
+        }
+    }
+}
+
+- (void)appendString:(NSString *)string toData:(NSMutableData *)data
+{
+    [data appendData:[string dataUsingEncoding:NSUTF8StringEncoding]];
+}
+
+- (BOOL)isConcurrent {
+    return NO;
+}
+
+- (BOOL)isExecuting {
+    return executing;
+}
+
+- (BOOL)isFinished {
+    return finished;
+}
+
+- (void)completeOperation {
+    [self willChangeValueForKey:@"isFinished"];
+    [self willChangeValueForKey:@"isExecuting"];
+    
+    executing = NO;
+    finished = YES;
+    
+    [self didChangeValueForKey:@"isExecuting"];
+    [self didChangeValueForKey:@"isFinished"];
+}
+
+- (void)executeCompletionBlockAccessToken:(NSString *)accessToken {
+    [self executeCompletionBlockAccessToken:accessToken error:nil];
+}
+
+- (void)executeCompletionBlockError:(NSError *)error {
+    [self executeCompletionBlockAccessToken:nil error:error];
+}
+
+- (void)executeCompletionBlockAccessToken:(NSString *)accessToken error:(NSError *)error {
+    if (self.operationCompletionBlock) {
+        void (^completionBlock)(NSString *accessToken, NSError *error);
+        completionBlock = self.operationCompletionBlock;
+        self.operationCompletionBlock = nil; // Prevent multiple execution if method on this request gets called inside completion block
+        completionBlock(accessToken, error);
+    }
+    [self completeOperation];
+}
+
+@end

Added: chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISOAuthToken.h
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISOAuthToken.h?rev=1795295&view=auto
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISOAuthToken.h (added)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISOAuthToken.h Tue May 16 10:33:32 2017
@@ -0,0 +1,35 @@
+/*
+  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>
+
+@interface CMISOAuthToken : NSObject
+
+@property (nonatomic, strong) NSString *accessToken;
+@property (nonatomic, strong) NSString *refreshToken;
+@property (nonatomic, strong) NSNumber *expirationTimestamp;
+
+- (instancetype)initWithAccessToken:(NSString *)accessToken refreshToken:(NSString *)refreshToken expirationTimestamp:(NSNumber *)expirationTimestamp;
+
+/*
+ Returns whether the access token is expired or not.
+ */
+- (BOOL)isExpired;
+
+@end

Added: chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISOAuthToken.m
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISOAuthToken.m?rev=1795295&view=auto
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISOAuthToken.m (added)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISOAuthToken.m Tue May 16 10:33:32 2017
@@ -0,0 +1,46 @@
+/*
+  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 "CMISOAuthToken.h"
+
+@implementation CMISOAuthToken
+
+- (instancetype)initWithAccessToken:(NSString *)accessToken refreshToken:(NSString *)refreshToken expirationTimestamp:(NSNumber *)expirationTimestamp
+{
+    self = [super init];
+    if (self) {
+        self.accessToken = accessToken;
+        self.refreshToken = refreshToken;
+        self.expirationTimestamp = expirationTimestamp;
+    }
+    return self;
+}
+
+- (BOOL)isExpired
+{
+    return [[NSDate date] timeIntervalSince1970] * 1000.0 >= [self.expirationTimestamp longLongValue]; // seconds to milliseconds
+}
+
+- (NSString *)description
+{
+    return [NSString stringWithFormat:@"CMISOAuthToken - accessToken: %@, refreshToken: %@, expirationTimestamp: %lld",
+            self.accessToken, self.refreshToken, [self.expirationTimestamp longLongValue]];
+}
+
+@end

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISObjectData.h
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISObjectData.h?rev=1795295&r1=1795294&r2=1795295&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISObjectData.h (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISObjectData.h Tue May 16 10:33:32 2017
@@ -26,6 +26,8 @@
 #import "CMISAcl.h"
 
 @class CMISRenditionData;
+@class CMISChangeEventInfo;
+@class CMISPolicyIdList;
 
 @interface CMISObjectData : CMISExtensionData
 
@@ -39,5 +41,7 @@
 @property (nonatomic, strong) NSArray *renditions; // An array containing CMISRenditionData objects
 @property (nonatomic, strong) NSArray *relationships; // An array containing CMISObjectData objects; Relationships from and to this object.
 @property (nonatomic, assign) BOOL isExactAcl; //TODO set this value also from atom
+@property (nonatomic, strong) CMISChangeEventInfo *changeEventInfo;
+@property (nonatomic, strong) CMISPolicyIdList *policyIds;
 
 @end

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISSessionParameters.h
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISSessionParameters.h?rev=1795295&r1=1795294&r2=1795295&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISSessionParameters.h (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISSessionParameters.h Tue May 16 10:33:32 2017
@@ -83,6 +83,18 @@ extern NSString * const kCMISSessionPara
  */
 extern NSString * const kCMISSessionParameterBackgroundNetworkSessionSharedContainerId;
 
+// --- OAuth ---
+
+extern NSString * const kCMISSessionParameterOAuthClientId;
+extern NSString * const kCMISSessionParameterOAuthClientSecret;
+extern NSString * const kCMISSessionParameterOAuthCode;
+extern NSString * const kCMISSessionParameterOAuthTokenEndpoint;
+extern NSString * const kCMISSessionParameterOAuthRedirectUri;
+
+extern NSString * const kCMISSessionParameterOAuthAccessToken;
+extern NSString * const kCMISSessionParameterOAuthRefreshToken;
+extern NSString * const kCMISSessionParameterOAuthExpirationTimestamp;
+extern NSString * const kCMISSessionParameterOAuthDefaultTokenLifetime;
 
 @interface CMISSessionParameters : NSObject
 

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISSessionParameters.m
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISSessionParameters.m?rev=1795295&r1=1795294&r2=1795295&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISSessionParameters.m (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISSessionParameters.m Tue May 16 10:33:32 2017
@@ -31,6 +31,20 @@ NSString * const kCMISSessionParameterUs
 NSString * const kCMISSessionParameterBackgroundNetworkSessionId = @"session_param_background_session_id";
 NSString * const kCMISSessionParameterBackgroundNetworkSessionSharedContainerId = @"session_param_background_session_shared_container_id";
 
+// --- OAuth ---
+
+NSString * const kCMISSessionParameterOAuthClientId = @"session_param_oauth_clientId";
+NSString * const kCMISSessionParameterOAuthClientSecret = @"session_param_oauth_client_secret";
+NSString * const kCMISSessionParameterOAuthCode = @"session_param_oauth_code";
+NSString * const kCMISSessionParameterOAuthTokenEndpoint = @"session_param_oauth_token_endpoint";
+NSString * const kCMISSessionParameterOAuthRedirectUri = @"session_param_oauth_redirect_uri";
+
+NSString * const kCMISSessionParameterOAuthAccessToken = @"session_param_oauth_access_token";
+NSString * const kCMISSessionParameterOAuthRefreshToken = @"session_param_oauth_refresh_token";
+NSString * const kCMISSessionParameterOAuthExpirationTimestamp = @"session_param_oauth_expiration_timestamp";
+NSString * const kCMISSessionParameterOAuthDefaultTokenLifetime = @"session_param_oauth_default_token_lifetime";
+
+
 @interface CMISSessionParameters ()
 @property (nonatomic, assign, readwrite) CMISBindingType bindingType;
 @property (nonatomic, strong, readwrite) NSMutableDictionary *sessionData;
@@ -56,8 +70,8 @@ NSString * const kCMISSessionParameterBa
 
 - (NSString *)description
 {
-    return [NSString stringWithFormat:@"bindingType: %li, username: %@, password: %@, atomPubUrl: %@",
-            (long)self.bindingType, self.username, self.password, self.atomPubUrl];
+    return [NSString stringWithFormat:@"bindingType: %li, username: %@, password: %@, atomPubUrl: %@, browserUrl: %@",
+            (long)self.bindingType, self.username, self.password, self.atomPubUrl, self.browserUrl];
 }
 
 - (NSArray *)allKeys

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISStandardAuthenticationProvider.m
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISStandardAuthenticationProvider.m?rev=1795295&r1=1795294&r2=1795295&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISStandardAuthenticationProvider.m (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISStandardAuthenticationProvider.m Tue May 16 10:33:32 2017
@@ -67,51 +67,6 @@
     // nothing to do in the default implementation
 }
 
-- (BOOL)canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
-{
-    // default implementation mimics default NSURLConnectionDelegate behavior
-    NSString *authenticationMethod = protectionSpace.authenticationMethod;
-    if ([authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate] && self.credential.identity) {
-        return YES; // client certificate requested and certificate identity available
-    }
-    if ([authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPBasic] && self.credential.user && self.credential.hasPassword) {
-        return YES; // basic authentication requested and username & password available
-    }
-    
-    return NO;
-}
-
-
-- (void)didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
-{
-    // nothing to do in the default implementation
-}
-
-
-- (void)didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
-{
-    if (challenge.previousFailureCount == 0) {
-        if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate] &&
-            self.credential.identity) {
-            CMISLogDebug(@"Authenticating with client certificate");
-            [challenge.sender useCredential:self.credential forAuthenticationChallenge:challenge];
-        } else if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPBasic] &&
-                   self.credential.user && self.credential.hasPassword) {
-            CMISLogDebug(@"Authenticating with username and password");
-            [challenge.sender useCredential:self.credential forAuthenticationChallenge:challenge];
-        } else if (challenge.proposedCredential) {
-            CMISLogDebug(@"Authenticating with proposed credential");
-            [challenge.sender useCredential:challenge.proposedCredential forAuthenticationChallenge:challenge];
-        } else {
-            CMISLogDebug(@"Authenticating without credential");
-            [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
-        }
-    } else {
-        CMISLogDebug(@"Authentication failed, cancelling logon");
-        [challenge.sender cancelAuthenticationChallenge:challenge];
-    }
-}
-
 - (void)didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
           completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
 {

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISStandardUntrustedSSLAuthenticationProvider.m
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISStandardUntrustedSSLAuthenticationProvider.m?rev=1795295&r1=1795294&r2=1795295&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISStandardUntrustedSSLAuthenticationProvider.m (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Common/CMISStandardUntrustedSSLAuthenticationProvider.m Tue May 16 10:33:32 2017
@@ -22,32 +22,12 @@
 
 @implementation CMISStandardUntrustedSSLAuthenticationProvider
 
-- (BOOL)canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
-{
-    if ([protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
-        return YES;
-    }
-    return [super canAuthenticateAgainstProtectionSpace:protectionSpace];
-}
-
 /**
  * NOTE: Once the TLS Session has been cached for an untrusted connection, that session will be reused
  * by iOS (since 6.0) and willSendRequestForAuthenticationChallenge: will not be called again until the session is cleared.
  * The session is cleared by an app restart or after around 10 minutes.
  * See Apple Technical Q&A QA1727 http://developer.apple.com/library/ios/#qa/qa1727 for full details.
  */
-- (void)didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
-{
-    if ((challenge.previousFailureCount == 0) &&
-        ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]))
-    {
-        CMISLogWarning(@"Allowing self-signed certificate");
-        [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
-    } else {
-        [super didReceiveAuthenticationChallenge:challenge];
-    }
-}
-
 - (void)didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
           completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
 {

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/ObjectiveCMIS.xcconfig
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/ObjectiveCMIS.xcconfig?rev=1795295&r1=1795294&r2=1795295&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/ObjectiveCMIS.xcconfig (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/ObjectiveCMIS.xcconfig Tue May 16 10:33:32 2017
@@ -16,7 +16,7 @@
 // under the License.
 
 // Library version number
-OBJECTIVECMIS_VERSION=0.5-dev
+OBJECTIVECMIS_VERSION=0.6-dev
 
 // Allow selected config variables to be accessible in code
 OTHER_CFLAGS=-DOBJECTIVECMIS_VERSION="@\"${OBJECTIVECMIS_VERSION}\""

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISDateUtil.m
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISDateUtil.m?rev=1795295&r1=1795294&r2=1795295&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISDateUtil.m (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISDateUtil.m Tue May 16 10:33:32 2017
@@ -35,7 +35,7 @@ static const NSString *kCalendarKey = @"
     if (dateFormatter == nil) {
         dateFormatter = [[NSDateFormatter alloc] init];
         dateFormatter.locale = [NSLocale systemLocale];
-        dateFormatter.calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; // ISO8601 calendar not available
+        dateFormatter.calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; // ISO8601 calendar not available
         NSTimeZone *timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
         dateFormatter.calendar.timeZone = timeZone;
         dateFormatter.timeZone = timeZone;
@@ -51,7 +51,7 @@ static const NSString *kCalendarKey = @"
     NSMutableDictionary *threadDictionary = [[NSThread currentThread] threadDictionary];
     NSCalendar *calendar = [threadDictionary objectForKey:kCalendarKey];
     if (calendar == nil) {
-        calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; // ISO8601 calendar not available
+        calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; // ISO8601 calendar not available
         calendar.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0]; // default for formats without time
         
         [threadDictionary setObject:calendar forKey:kCalendarKey];

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISDefaultNetworkProvider.h
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISDefaultNetworkProvider.h?rev=1795295&r1=1795294&r2=1795295&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISDefaultNetworkProvider.h (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISDefaultNetworkProvider.h Tue May 16 10:33:32 2017
@@ -18,3 +18,11 @@
 @interface CMISDefaultNetworkProvider : NSObject <CMISNetworkProvider>
 
 @end
+
+@interface CMISDefaultNetworkProvider (Protected)
+
++ (NSMutableURLRequest *)createRequestForUrl:(NSURL *)url
+                                  httpMethod:(CMISHttpRequestMethod)httpRequestMethod
+                                     session:(CMISBindingSession *)session;
+
+@end

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISDefaultNetworkProvider.m
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISDefaultNetworkProvider.m?rev=1795295&r1=1795294&r2=1795295&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISDefaultNetworkProvider.m (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISDefaultNetworkProvider.m Tue May 16 10:33:32 2017
@@ -19,12 +19,6 @@
 #import "CMISHttpUploadRequest.h"
 #import "CMISLog.h"
 
-@interface CMISDefaultNetworkProvider ()
-+ (NSMutableURLRequest *)createRequestForUrl:(NSURL *)url
-                                  httpMethod:(CMISHttpRequestMethod)httpRequestMethod
-                                     session:(CMISBindingSession *)session;
-@end
-
 @implementation CMISDefaultNetworkProvider
 #pragma mark block based methods
 

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISHttpDownloadRequest.m
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISHttpDownloadRequest.m?rev=1795295&r1=1795294&r2=1795295&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISHttpDownloadRequest.m (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISHttpDownloadRequest.m Tue May 16 10:33:32 2017
@@ -206,29 +206,40 @@
     }
     
     self.bytesDownloaded = 0;
-    
-    // set up output stream if available
-    if (self.outputStream) { // otherwise store data in memory in self.data
-        // create file for downloaded content
-        BOOL isStreamReady = self.outputStream.streamStatus == NSStreamStatusOpen;
-        if (!isStreamReady) {
-            [self.outputStream open];
-            isStreamReady = self.outputStream.streamStatus == NSStreamStatusOpen;
+    if ([response isKindOfClass:NSHTTPURLResponse.class] && [CMISHttpRequest isErrorResponse:((NSHTTPURLResponse *)response).statusCode httpRequestMethod:self.requestMethod]) {
+        // we are receiving an error response -> do not write error response body to outputStream. Instead, store data in memory in self.data
+        if (self.outputStream) { // clean up
+            BOOL isStreamOpen = self.outputStream.streamStatus == NSStreamStatusOpen;
+            if (isStreamOpen) {
+                [self.outputStream close];
+            }
+            self.outputStream = nil; // remove reference so we store data in memory in self.data
         }
-        
-        if (isStreamReady) {
-            [super URLSession:session dataTask:dataTask didReceiveResponse:response completionHandler:completionHandler];
-        } else {
-            [session invalidateAndCancel];
+        [super URLSession:session dataTask:dataTask didReceiveResponse:response completionHandler:completionHandler];
+    } else {
+        // set up output stream if available
+        if (self.outputStream) { // otherwise store data in memory in self.data
+            // create file for downloaded content
+            BOOL isStreamReady = self.outputStream.streamStatus == NSStreamStatusOpen;
+            if (!isStreamReady) {
+                [self.outputStream open];
+                isStreamReady = self.outputStream.streamStatus == NSStreamStatusOpen;
+            }
             
-            if (self.completionBlock)
-            {
-                NSError *cmisError = [CMISErrors createCMISErrorWithCode:kCMISErrorCodeStorage
-                                                     detailedDescription:@"Could not open output stream"];
+            if (isStreamReady) {
+                [super URLSession:session dataTask:dataTask didReceiveResponse:response completionHandler:completionHandler];
+            } else {
+                [session invalidateAndCancel];
                 
-                // call the completion block on the original thread
-                if (self.originalThread) {
-                    [self performSelector:@selector(executeCompletionBlockError:) onThread:self.originalThread withObject:cmisError waitUntilDone:NO];
+                if (self.completionBlock)
+                {
+                    NSError *cmisError = [CMISErrors createCMISErrorWithCode:kCMISErrorCodeStorage
+                                                         detailedDescription:@"Could not open output stream"];
+                    
+                    // call the completion block on the original thread
+                    if (self.originalThread) {
+                        [self performSelector:@selector(executeCompletionBlockError:) onThread:self.originalThread withObject:cmisError waitUntilDone:NO];
+                    }
                 }
             }
         }

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISHttpRequest.h
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISHttpRequest.h?rev=1795295&r1=1795294&r2=1795295&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISHttpRequest.h (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISHttpRequest.h Tue May 16 10:33:32 2017
@@ -37,10 +37,10 @@
 @property (nonatomic, weak) NSThread *originalThread;
 
 /**
- * starts a URL request for given HTTP method 
+ * starts a URL request for given HTTP method
  * @param requestBody (optional)
  * @param additionalHeaders (optional)
- * @param authenticationProvider (required)
+ * @param session (required)
  * completionBlock returns a CMISHTTPResponse object or nil if unsuccessful
  */
 + (id)startRequest:(NSMutableURLRequest *)urlRequest
@@ -68,4 +68,17 @@
 /// Call completion block with error returned from server
 - (void)executeCompletionBlockError:(NSError*)error;
 
+-(void) didCompleteWithError:(NSError *)error;
+
+- (BOOL)callCompletionBlockOnOriginalThread;
+
++ (BOOL)checkStatusCodeForResponse:(CMISHttpResponse *)response httpRequestMethod:(CMISHttpRequestMethod)httpRequestMethod error:(NSError **)error;
+
+@end
+
+@interface CMISHttpRequest (Protected)
+
++ (BOOL)isErrorResponse:(NSInteger)statusCode httpRequestMethod:(CMISHttpRequestMethod)httpRequestMethod;
+- (BOOL)shouldApplyHttpHeaders;
+
 @end

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISHttpRequest.m
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISHttpRequest.m?rev=1795295&r1=1795294&r2=1795295&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISHttpRequest.m (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISHttpRequest.m Tue May 16 10:33:32 2017
@@ -23,6 +23,7 @@
 #import "CMISLog.h"
 #import "CMISReachability.h"
 #import "CMISConstants.h"
+#import "CMISURLSessionUtil.h"
 
 //Exception names as returned in the <!--exception> tag
 NSString * const kCMISExceptionInvalidArgument         = @"invalidArgument";
@@ -62,7 +63,6 @@ NSString * const kCMISExceptionVersionin
     return httpRequest;
 }
 
-
 - (id)initWithHttpMethod:(CMISHttpRequestMethod)httpRequestMethod
          completionBlock:(void (^)(CMISHttpResponse *httpResponse, NSError *error))completionBlock
 {
@@ -89,8 +89,6 @@ NSString * const kCMISExceptionVersionin
         }
     }
     
-    BOOL startedRequest = NO;
-    
     if (self.requestBody) {
         if ([CMISLog sharedInstance].logLevel == CMISLogLevelTrace) {
             CMISLogTrace(@"Request body: %@", [[NSString alloc] initWithData:self.requestBody encoding:NSUTF8StringEncoding]);
@@ -99,59 +97,64 @@ NSString * const kCMISExceptionVersionin
         [urlRequest setHTTPBody:self.requestBody];
     }
     
-    [self.session.authenticationProvider.httpHeadersToApply enumerateKeysAndObjectsUsingBlock:^(NSString *headerName, NSString *header, BOOL *stop) {
-        [urlRequest addValue:header forHTTPHeaderField:headerName];
-        if ([CMISLog sharedInstance].logLevel == CMISLogLevelTrace) {
-            CMISLogTrace(@"Added header: %@ with value: %@", headerName, header);
-        }
-    }];
-    
-    [self.additionalHeaders enumerateKeysAndObjectsUsingBlock:^(NSString *headerName, NSString *header, BOOL *stop) {
-        [urlRequest addValue:header forHTTPHeaderField:headerName];
+    void (^continueWithStartRequest)(NSDictionary*) = ^(NSDictionary *headers) {
+        [headers enumerateKeysAndObjectsUsingBlock:^(NSString *headerName, id header, BOOL *stop) {
+            if ([header isKindOfClass:NSSet.class]) {
+                for (NSString *headerValue in header) {
+                    [urlRequest addValue:headerValue forHTTPHeaderField:headerName];
+                }
+            } else {
+                [urlRequest addValue:header forHTTPHeaderField:headerName];
+            }
+        }];
+        
+        [self.additionalHeaders enumerateKeysAndObjectsUsingBlock:^(NSString *headerName, NSString *header, BOOL *stop) {
+            [urlRequest addValue:header forHTTPHeaderField:headerName];
+        }];
+        
         if ([CMISLog sharedInstance].logLevel == CMISLogLevelTrace) {
-            CMISLogTrace(@"Added header: %@ with value: %@", headerName, header);
+            CMISLogTrace(@"Added headers: %@", urlRequest.allHTTPHeaderFields);
         }
-    }];
-    
-    // determine the type of session configuration to create
-    NSURLSessionConfiguration *sessionConfiguration = nil;
-    id useBackgroundSession = [self.session objectForKey:kCMISSessionParameterUseBackgroundNetworkSession];
-    if (useBackgroundSession && [useBackgroundSession boolValue]) {
-        // get session and container identifiers from session
-        NSString *backgroundId = [self.session objectForKey:kCMISSessionParameterBackgroundNetworkSessionId
-                                               defaultValue:kCMISDefaultBackgroundNetworkSessionId];
-        NSString *containerId = [self.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;
+            
+        // create session and task
+        self.urlSession = [CMISURLSessionUtil internalUrlSessionWithParameters:self.session delegate:self];
+        self.sessionTask = [self taskForRequest:urlRequest];
         
-        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];
-    }
-    
-    // create session and task
-    self.urlSession = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:nil];
-    self.sessionTask = [self taskForRequest:urlRequest];
+        if (self.sessionTask) {
+            // start the task
+            [self.sessionTask resume];
+        } else {
+            if (self.completionBlock) {
+                NSString *detailedDescription = [NSString stringWithFormat:@"Could not create network session for %@", urlRequest.URL];
+                NSError *cmisError = [CMISErrors createCMISErrorWithCode:kCMISErrorCodeConnection detailedDescription:detailedDescription];
+                [self executeCompletionBlockResponse:nil error:cmisError];
+            }
+        }
+    };
     
-    if (self.sessionTask) {
-        // start the task
-        [self.sessionTask resume];
-        startedRequest = YES;
-    } else {
-        if (self.completionBlock) {
-            NSString *detailedDescription = [NSString stringWithFormat:@"Could not create network session for %@", urlRequest.URL];
-            NSError *cmisError = [CMISErrors createCMISErrorWithCode:kCMISErrorCodeConnection detailedDescription:detailedDescription];
-            [self executeCompletionBlockResponse:nil error:cmisError];
+    if ([self shouldApplyHttpHeaders]) {
+        if ([self.session.authenticationProvider respondsToSelector:@selector(asyncHttpHeadersToApply:)]) {
+            [self.session.authenticationProvider asyncHttpHeadersToApply:^(NSDictionary *headers, NSError *cmisError) {
+                if (cmisError) {;
+                    [self executeCompletionBlockResponse:nil error:cmisError];
+                } else {
+                    continueWithStartRequest(headers);
+                }
+            }];
+        } else {
+            continueWithStartRequest(self.session.authenticationProvider.httpHeadersToApply);
         }
+    } else { // when OAuth tokens are fetched we do not apply the headers of the CMISOAuthAuthenticationProvider
+        continueWithStartRequest(nil);
     }
-    
-    return startedRequest;
+
+    return YES; //TODO check if we need to remove the return value as it cannot be used anymore because of async behavior
+}
+
+// will be overwritten by CMISOAuthHttpRequest
+- (BOOL)shouldApplyHttpHeaders
+{
+    return YES;
 }
 
 - (NSURLSessionTask *)taskForRequest:(NSURLRequest *)request
@@ -185,13 +188,20 @@ NSString * const kCMISExceptionVersionin
 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
 {
     [self.session.authenticationProvider updateWithHttpURLResponse:self.response];
+    [self didCompleteWithError:error];
+}
 
+-(void) didCompleteWithError:(NSError *)error {
     if (self.completionBlock) {
         
         NSError *cmisError = nil;
         CMISHttpResponse *httpResponse = nil;
         
         if (error) {
+            if ([CMISLog sharedInstance].logLevel == CMISLogLevelTrace) {
+                CMISLogTrace(@"Request did complete with error: %@", error);
+            }
+            
             CMISErrorCodes cmisErrorCode = kCMISErrorCodeConnection;
             
             // swap error code if necessary
@@ -205,17 +215,18 @@ NSString * const kCMISExceptionVersionin
         } else {
             // no error returned but we also need to check response code
             httpResponse = [CMISHttpResponse responseUsingURLHTTPResponse:self.response data:self.responseBody];
-            if (![self checkStatusCodeForResponse:httpResponse httpRequestMethod:self.requestMethod error:&cmisError]) {
+            if (![CMISHttpRequest checkStatusCodeForResponse:httpResponse httpRequestMethod:self.requestMethod error:&cmisError]) {
                 httpResponse = nil;
             }
         }
-        // call the completion block on the original thread
-        if (self.originalThread) {
+        if ([self callCompletionBlockOnOriginalThread] && self.originalThread) { // call the completion block on the original thread
             if(cmisError) {
                 [self performSelector:@selector(executeCompletionBlockError:) onThread:self.originalThread withObject:cmisError waitUntilDone:NO];
             } else {
                 [self performSelector:@selector(executeCompletionBlockResponse:) onThread:self.originalThread withObject:httpResponse waitUntilDone:NO];
             }
+        } else {
+            [self executeCompletionBlockResponse:httpResponse error:cmisError];
         }
     }
     
@@ -244,17 +255,13 @@ NSString * const kCMISExceptionVersionin
     [self.session.authenticationProvider didReceiveChallenge:challenge completionHandler:completionHandler];
 }
 
-- (BOOL)checkStatusCodeForResponse:(CMISHttpResponse *)response httpRequestMethod:(CMISHttpRequestMethod)httpRequestMethod error:(NSError **)error
++ (BOOL)checkStatusCodeForResponse:(CMISHttpResponse *)response httpRequestMethod:(CMISHttpRequestMethod)httpRequestMethod error:(NSError **)error
 {
     if ([CMISLog sharedInstance].logLevel == CMISLogLevelTrace) {
-        CMISLogTrace(@"Response status code: %d", (int)response.statusCode);
-        CMISLogTrace(@"Response body: %@", [[NSString alloc] initWithData:response.data encoding:NSUTF8StringEncoding]);
+        CMISLogTrace(@"Response status code: %d, Response body: %@", (int)response.statusCode, [[NSString alloc] initWithData:response.data encoding:NSUTF8StringEncoding]);
     }
     
-    if ( (httpRequestMethod == HTTP_GET && response.statusCode != 200 && response.statusCode != 206)
-        || (httpRequestMethod == HTTP_POST && response.statusCode != 200 && response.statusCode != 201)
-        || (httpRequestMethod == HTTP_DELETE && response.statusCode != 204)
-        || (httpRequestMethod == HTTP_PUT && ((response.statusCode < 200 || response.statusCode > 299)))) {
+    if ([CMISHttpRequest isErrorResponse:response.statusCode httpRequestMethod:httpRequestMethod]) {
         if (error) {
             NSString *exception = response.exception;
             NSString *errorMessage = response.errorMessage;
@@ -330,6 +337,19 @@ NSString * const kCMISExceptionVersionin
     return YES;
 }
 
++ (BOOL)isErrorResponse:(NSInteger)statusCode httpRequestMethod:(CMISHttpRequestMethod)httpRequestMethod
+{
+    return (httpRequestMethod == HTTP_GET && statusCode != 200 && statusCode != 206)
+            || (httpRequestMethod == HTTP_POST && statusCode != 200 && statusCode != 201)
+            || (httpRequestMethod == HTTP_DELETE && statusCode != 204)
+            || (httpRequestMethod == HTTP_PUT && ((statusCode < 200 || statusCode > 299)));
+}
+
+-(BOOL)callCompletionBlockOnOriginalThread
+{
+    return YES;
+}
+
 - (void)executeCompletionBlockResponse:(CMISHttpResponse*)response {
     [self executeCompletionBlockResponse:response error:nil];
 }

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISHttpResponse.h
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISHttpResponse.h?rev=1795295&r1=1795294&r2=1795295&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISHttpResponse.h (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Utils/CMISHttpResponse.h Tue May 16 10:33:32 2017
@@ -40,3 +40,9 @@
 - (NSString*)errorMessage;
 
 @end
+
+@interface CMISHttpResponse (Protected)
+
+- (NSString*)responseValueForKey:(NSString *)key;
+
+@end



Mime
View raw message