blob: 450153c4f395c9499c8698c68c89a827bc015921 [file] [log] [blame]
/*
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 "CMISSession.h"
#import "CMISConstants.h"
#import "CMISObjectConverter.h"
#import "CMISStandardAuthenticationProvider.h"
#import "CMISBindingFactory.h"
#import "CMISObjectList.h"
#import "CMISQueryResult.h"
#import "CMISErrors.h"
#import "CMISOperationContext.h"
#import "CMISPagedResult.h"
#import "CMISTypeDefinition.h"
#import "CMISNetworkProvider.h"
#import "CMISDefaultNetworkProvider.h"
@interface CMISSession ()
@property (nonatomic, strong, readwrite) CMISObjectConverter *objectConverter;
@property (nonatomic, assign, readwrite, getter = isAuthenticated) BOOL authenticated;
@property (nonatomic, strong, readwrite) id<CMISBinding> binding;
@property (nonatomic, strong, readwrite) CMISRepositoryInfo *repositoryInfo;
// Returns a CMISSession using the given session parameters.
- (id)initWithSessionParameters:(CMISSessionParameters *)sessionParameters;
// Authenticates using the CMISSessionParameters and returns if the authentication was succesful
- (void)authenticateWithCompletionBlock:(void (^)(CMISSession *session, NSError * error))completionBlock;
@end
@interface CMISSession (PrivateMethods)
- (BOOL)authenticateAndReturnError:(NSError **)error;
@end
@implementation CMISSession
#pragma mark -
#pragma mark Setup
+ (void)arrayOfRepositories:(CMISSessionParameters *)sessionParameters completionBlock:(void (^)(NSArray *repositories, NSError *error))completionBlock
{
CMISSession *session = [[CMISSession alloc] initWithSessionParameters:sessionParameters];
// TODO: validate session parameters?
// return list of repositories
[session.binding.repositoryService retrieveRepositoriesWithCompletionBlock:completionBlock];
}
+ (void)connectWithSessionParameters:(CMISSessionParameters *)sessionParameters
completionBlock:(void (^)(CMISSession *session, NSError * error))completionBlock
{
CMISSession *session = [[CMISSession alloc] initWithSessionParameters:sessionParameters];
if (session) {
[session authenticateWithCompletionBlock:completionBlock];
} else {
completionBlock(nil, [CMISErrors createCMISErrorWithCode:kCMISErrorCodeInvalidArgument
withDetailedDescription:@"Not enough session parameters to connect"]);
}
}
#pragma internal authentication methods
- (id)initWithSessionParameters:(CMISSessionParameters *)sessionParameters
{
self = [super init];
if (self) {
self.sessionParameters = sessionParameters;
self.authenticated = NO;
// setup authentication provider if not present
if (self.sessionParameters.authenticationProvider == nil) {
NSString *username = self.sessionParameters.username;
NSString *password = self.sessionParameters.password;
if (username == nil || password == nil) {
log(@"No username or password provided for standard authentication provider");
return nil;
}
self.sessionParameters.authenticationProvider = [[CMISStandardAuthenticationProvider alloc] initWithUsername:username
andPassword:password];
}
if (self.sessionParameters.networkProvider == nil) {
self.sessionParameters.networkProvider = [[CMISDefaultNetworkProvider alloc] init];
}
// create the binding the session will use
CMISBindingFactory *bindingFactory = [[CMISBindingFactory alloc] init];
self.binding = [bindingFactory bindingWithParameters:sessionParameters];
id objectConverterClassValue = [self.sessionParameters objectForKey:kCMISSessionParameterObjectConverterClassName];
if (objectConverterClassValue != nil && [objectConverterClassValue isKindOfClass:[NSString class]]) {
NSString *objectConverterClassName = (NSString *)objectConverterClassValue;
log(@"Using a custom object converter class: %@", objectConverterClassName);
self.objectConverter = [[NSClassFromString(objectConverterClassName) alloc] initWithSession:self];
} else { //default
self.objectConverter = [[CMISObjectConverter alloc] initWithSession:self];
}
// TODO: setup locale
// TODO: setup default session parameters
// TODO: setup caches
}
return self;
}
- (void)authenticateWithCompletionBlock:(void (^)(CMISSession *session, NSError * error))completionBlock
{
// TODO: validate session parameters, extract the checks below?
// check repository id is present
if (self.sessionParameters.repositoryId == nil) {
NSError *error = [CMISErrors createCMISErrorWithCode:kCMISErrorCodeInvalidArgument
withDetailedDescription:@"Must provide repository id"];
log(@"Error: %@", error.description);
completionBlock(nil, error);
return;
}
if (self.sessionParameters.authenticationProvider == nil) {
NSError *error = [CMISErrors createCMISErrorWithCode:kCMISErrorCodeUnauthorized withDetailedDescription:@"Must provide authentication provider"];
log(@"Error: %@", error.description);
completionBlock(nil, error);
return;
}
// TODO: use authentication provider to make sure we have enough credentials, it may need to make another call to get a ticket or do handshake i.e. NTLM.
// get repository info
[self.binding.repositoryService retrieveRepositoryInfoForId:self.sessionParameters.repositoryId completionBlock:^(CMISRepositoryInfo *repositoryInfo, NSError *error) {
self.repositoryInfo = repositoryInfo;
if (self.repositoryInfo == nil) {
if (error) {
log(@"Error because repositoryInfo is nil: %@", error.description);
completionBlock(nil, [CMISErrors cmisError:error withCMISErrorCode:kCMISErrorCodeInvalidArgument]);
} else {
completionBlock(nil, [CMISErrors createCMISErrorWithCode:kCMISErrorCodeInvalidArgument
withDetailedDescription:@"Could not fetch repository information"]);
}
} else {
// no errors have occurred so set authenticated flag and return success flag
self.authenticated = YES;
completionBlock(self, nil);
}
}];
}
#pragma mark CMIS operations
- (void)retrieveRootFolderWithCompletionBlock:(void (^)(CMISFolder *folder, NSError *error))completionBlock
{
[self retrieveFolderWithOperationContext:[CMISOperationContext defaultOperationContext] completionBlock:completionBlock];
}
- (void)retrieveFolderWithOperationContext:(CMISOperationContext *)operationContext completionBlock:(void (^)(CMISFolder *folder, NSError *error))completionBlock
{
NSString *rootFolderId = self.repositoryInfo.rootFolderId;
[self retrieveObject:rootFolderId withOperationContext:operationContext completionBlock:^(CMISObject *rootFolder, NSError *error) {
if (rootFolder != nil && ![rootFolder isKindOfClass:[CMISFolder class]]) {
completionBlock(nil, [CMISErrors createCMISErrorWithCode:kCMISErrorCodeRuntime withDetailedDescription:@"Root folder object is not a folder!"]);
} else {
completionBlock((CMISFolder *)rootFolder, nil);
}
}];
}
- (void)retrieveObject:(NSString *)objectId completionBlock:(void (^)(CMISObject *object, NSError *error))completionBlock
{
[self retrieveObject:objectId withOperationContext:[CMISOperationContext defaultOperationContext] completionBlock:completionBlock];
}
- (void)retrieveObject:(NSString *)objectId withOperationContext:(CMISOperationContext *)operationContext completionBlock:(void (^)(CMISObject *object, NSError *error))completionBlock
{
if (objectId == nil) {
completionBlock(nil, [CMISErrors createCMISErrorWithCode:kCMISErrorCodeInvalidArgument withDetailedDescription:@"Must provide object id"]);
return;
}
// TODO: cache the object
[self.binding.objectService retrieveObject:objectId
withFilter:operationContext.filterString
andIncludeRelationShips:operationContext.includeRelationShips
andIncludePolicyIds:operationContext.includePolicies
andRenditionFilder:operationContext.renditionFilterString
andIncludeACL:operationContext.includeACLs
andIncludeAllowableActions:operationContext.includeAllowableActions
completionBlock:^(CMISObjectData *objectData, NSError *error) {
if (error) {
completionBlock(nil, [CMISErrors cmisError:error withCMISErrorCode:kCMISErrorCodeObjectNotFound]);
} else {
CMISObject *object = nil;
if (objectData) {
object = [self.objectConverter convertObject:objectData];
}
completionBlock(object, nil);
}
}];
}
- (void)retrieveObjectByPath:(NSString *)path completionBlock:(void (^)(CMISObject *object, NSError *error))completionBlock
{
[self retrieveObjectByPath:path withOperationContext:[CMISOperationContext defaultOperationContext] completionBlock:completionBlock];
}
- (void)retrieveObjectByPath:(NSString *)path withOperationContext:(CMISOperationContext *)operationContext completionBlock:(void (^)(CMISObject *object, NSError *error))completionBlock
{
[self.binding.objectService retrieveObjectByPath:path
withFilter:operationContext.filterString
andIncludeRelationShips:operationContext.includeRelationShips
andIncludePolicyIds:operationContext.includePolicies
andRenditionFilder:operationContext.renditionFilterString
andIncludeACL:operationContext.includeACLs
andIncludeAllowableActions:operationContext.includeAllowableActions
completionBlock:^(CMISObjectData *objectData, NSError *error) {
if (objectData != nil && error == nil) {
completionBlock([self.objectConverter convertObject:objectData], nil);
} else {
if (error == nil) {
error = [[NSError alloc] init]; // TODO: create a proper error object
}
completionBlock(nil, error);
}
}];
}
- (void)retrieveTypeDefinition:(NSString *)typeId completionBlock:(void (^)(CMISTypeDefinition *typeDefinition, NSError *error))completionBlock
{
return [self.binding.repositoryService retrieveTypeDefinition:typeId completionBlock:completionBlock];
}
- (void)query:(NSString *)statement searchAllVersions:(BOOL)searchAllVersion completionBlock:(void (^)(CMISPagedResult *pagedResult, NSError *error))completionBlock
{
[self query:statement searchAllVersions:searchAllVersion operationContext:[CMISOperationContext defaultOperationContext] completionBlock:completionBlock];
}
- (void)query:(NSString *)statement searchAllVersions:(BOOL)searchAllVersion
operationContext:(CMISOperationContext *)operationContext
completionBlock:(void (^)(CMISPagedResult *pagedResult, NSError *error))completionBlock
{
CMISFetchNextPageBlock fetchNextPageBlock = ^(int skipCount, int maxItems, CMISFetchNextPageBlockCompletionBlock pageBlockCompletionBlock){
// Fetch results through discovery service
[self.binding.discoveryService query:statement
searchAllVersions:searchAllVersion
includeRelationShips:operationContext.includeRelationShips
renditionFilter:operationContext.renditionFilterString
includeAllowableActions:operationContext.includeAllowableActions
maxItems:[NSNumber numberWithInt:maxItems]
skipCount:[NSNumber numberWithInt:skipCount]
completionBlock:^(CMISObjectList *objectList, NSError *error) {
if (error) {
pageBlockCompletionBlock(nil, [CMISErrors cmisError:error withCMISErrorCode:kCMISErrorCodeRuntime]);
} else {
// Fill up return result
CMISFetchNextPageBlockResult *result = [[CMISFetchNextPageBlockResult alloc] init];
result.hasMoreItems = objectList.hasMoreItems;
result.numItems = objectList.numItems;
NSMutableArray *resultArray = [[NSMutableArray alloc] init];
result.resultArray = resultArray;
for (CMISObjectData *objectData in objectList.objects) {
[resultArray addObject:[CMISQueryResult queryResultUsingCmisObjectData:objectData andWithSession:self]];
}
pageBlockCompletionBlock(result, nil);
}
}];
};
[CMISPagedResult pagedResultUsingFetchBlock:fetchNextPageBlock
andLimitToMaxItems:operationContext.maxItemsPerPage
andStartFromSkipCount:operationContext.skipCount
completionBlock:^(CMISPagedResult *result, NSError *error) {
// Return nil and populate error in case something went wrong
if (error) {
completionBlock(nil, [CMISErrors cmisError:error withCMISErrorCode:kCMISErrorCodeRuntime]);
} else {
completionBlock(result, nil);
}
}];
}
- (void)queryObjectsWithTypeDefinition:(CMISTypeDefinition *)typeDefinition
withWhereClause:(NSString *)whereClause
searchAllVersions:(BOOL)searchAllVersion
operationContext:(CMISOperationContext *)operationContext
completionBlock:(void (^)(CMISPagedResult *result, NSError *error))completionBlock
{
// Creating the cmis query using the input params
NSMutableString *statement = [[NSMutableString alloc] init];
// Filter
[statement appendFormat:@"SELECT %@", (operationContext.filterString != nil ? operationContext.filterString : @"*")];
// Type
[statement appendFormat:@" FROM %@", typeDefinition.queryName];
// Where
if (whereClause != nil) {
[statement appendFormat:@" WHERE %@", whereClause];
}
// Order by
if (operationContext.orderBy != nil) {
[statement appendFormat:@" ORDER BY %@", operationContext.orderBy];
}
// Fetch block for paged results
CMISFetchNextPageBlock fetchNextPageBlock = ^(int skipCount, int maxItems, CMISFetchNextPageBlockCompletionBlock pageBlockCompletionBlock)
{
// Fetch results through discovery service
[self.binding.discoveryService query:statement
searchAllVersions:searchAllVersion
includeRelationShips:operationContext.includeRelationShips
renditionFilter:operationContext.renditionFilterString
includeAllowableActions:operationContext.includeAllowableActions
maxItems:[NSNumber numberWithInt:maxItems]
skipCount:[NSNumber numberWithInt:skipCount]
completionBlock:^(CMISObjectList *objectList, NSError *error) {
if (error) {
pageBlockCompletionBlock(nil, [CMISErrors cmisError:error withCMISErrorCode:kCMISErrorCodeRuntime]);
} else {
// Fill up return result
CMISFetchNextPageBlockResult *result = [[CMISFetchNextPageBlockResult alloc] init];
result.hasMoreItems = objectList.hasMoreItems;
result.numItems = objectList.numItems;
NSMutableArray *resultArray = [[NSMutableArray alloc] init];
result.resultArray = resultArray;
for (CMISObjectData *objectData in objectList.objects)
{
[resultArray addObject:[self.objectConverter convertObject:objectData]];
}
pageBlockCompletionBlock(result, nil);
}
}];
};
[CMISPagedResult pagedResultUsingFetchBlock:fetchNextPageBlock
andLimitToMaxItems:operationContext.maxItemsPerPage
andStartFromSkipCount:operationContext.skipCount
completionBlock:^(CMISPagedResult *result, NSError *error) {
// Return nil and populate error in case something went wrong
if (error) {
completionBlock(nil, [CMISErrors cmisError:error withCMISErrorCode:kCMISErrorCodeRuntime]);
} else {
completionBlock(result, nil);
}
}];
}
- (void)queryObjectsWithTypeid:(NSString *)typeId
withWhereClause:(NSString *)whereClause
searchAllVersions:(BOOL)searchAllVersion
operationContext:(CMISOperationContext *)operationContext
completionBlock:(void (^)(CMISPagedResult *result, NSError *error))completionBlock
{
[self retrieveTypeDefinition:typeId
completionBlock:^(CMISTypeDefinition *typeDefinition, NSError *internalError) {
if (internalError != nil) {
NSError *error = [CMISErrors cmisError:internalError withCMISErrorCode:kCMISErrorCodeRuntime];
completionBlock(nil, error);
} else {
[self queryObjectsWithTypeDefinition:typeDefinition
withWhereClause:whereClause
searchAllVersions:searchAllVersion
operationContext:operationContext
completionBlock:completionBlock];
}
}];
}
- (void)createFolder:(NSDictionary *)properties
inFolder:(NSString *)folderObjectId
completionBlock:(void (^)(NSString *objectId, NSError *error))completionBlock
{
[self.objectConverter convertProperties:properties
forObjectTypeId:[properties objectForKey:kCMISPropertyObjectTypeId]
completionBlock:^(CMISProperties *convertedProperties, NSError *error) {
if (error) {
completionBlock(nil, [CMISErrors cmisError:error withCMISErrorCode:kCMISErrorCodeRuntime]);
} else {
[self.binding.objectService createFolderInParentFolder:folderObjectId
withProperties:convertedProperties
completionBlock:^(NSString *objectId, NSError *error) {
completionBlock(objectId, error);
}];
}
}];
}
- (CMISRequest*)downloadContentOfCMISObject:(NSString *)objectId
toFile:(NSString *)filePath
completionBlock:(void (^)(NSError *error))completionBlock
progressBlock:(void (^)(unsigned long long bytesDownloaded, unsigned long long bytesTotal))progressBlock
{
return [self.binding.objectService downloadContentOfObject:objectId
withStreamId:nil
toFile:filePath
completionBlock:completionBlock
progressBlock:progressBlock];
}
- (CMISRequest*)downloadContentOfCMISObject:(NSString *)objectId
toOutputStream:(NSOutputStream *)outputStream
completionBlock:(void (^)(NSError *error))completionBlock
progressBlock:(void (^)(unsigned long long bytesDownloaded, unsigned long long bytesTotal))progressBlock
{
return [self.binding.objectService downloadContentOfObject:objectId
withStreamId:nil
toOutputStream:outputStream
completionBlock:completionBlock
progressBlock:progressBlock];
}
- (void)createDocumentFromFilePath:(NSString *)filePath withMimeType:(NSString *)mimeType
withProperties:(NSDictionary *)properties inFolder:(NSString *)folderObjectId
completionBlock:(void (^)(NSString *objectId, NSError *error))completionBlock
progressBlock:(void (^)(unsigned long long bytesUploaded, unsigned long long bytesTotal))progressBlock
{
[self.objectConverter convertProperties:properties
forObjectTypeId:[properties objectForKey:kCMISPropertyObjectTypeId]
completionBlock:^(CMISProperties *convertedProperties, NSError *error) {
if (error) {
log(@"Could not convert properties: %@", error.description);
if (completionBlock) {
completionBlock(nil, [CMISErrors cmisError:error withCMISErrorCode:kCMISErrorCodeRuntime]);
}
} else {
[self.binding.objectService createDocumentFromFilePath:filePath
withMimeType:mimeType
withProperties:convertedProperties
inFolder:folderObjectId
completionBlock:completionBlock
progressBlock:progressBlock];
}
}];
}
- (void)createDocumentFromInputStream:(NSInputStream *)inputStream
withMimeType:(NSString *)mimeType
withProperties:(NSDictionary *)properties
inFolder:(NSString *)folderObjectId
bytesExpected:(unsigned long long)bytesExpected
completionBlock:(void (^)(NSString *objectId, NSError *error))completionBlock
progressBlock:(void (^)(unsigned long long bytesUploaded, unsigned long long bytesTotal))progressBlock
{
[self.objectConverter convertProperties:properties
forObjectTypeId:[properties objectForKey:kCMISPropertyObjectTypeId]
completionBlock:^(CMISProperties *convertedProperties, NSError *error) {
if (error) {
log(@"Could not convert properties: %@", error.description);
if (completionBlock) {
completionBlock(nil, [CMISErrors cmisError:error withCMISErrorCode:kCMISErrorCodeRuntime]);
}
} else {
[self.binding.objectService createDocumentFromInputStream:inputStream
withMimeType:mimeType
withProperties:convertedProperties
inFolder:folderObjectId
bytesExpected:bytesExpected
completionBlock:completionBlock
progressBlock:progressBlock];
}
}];
}
@end