blob: bd871e5f8ac57d65285b47e801a93f2a1178d127 [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 "CMISRequest.h"
#import "CMISPagedResult.h"
#import "CMISTypeDefinition.h"
#import "CMISNetworkProvider.h"
#import "CMISDefaultNetworkProvider.h"
#import "CMISLog.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;
@property (nonatomic, strong, readwrite) NSMutableDictionary *typeCache;
// Returns a CMISSession using the given session parameters.
- (id)initWithSessionParameters:(CMISSessionParameters *)sessionParameters;
// Authenticates using the CMISSessionParameters and returns if the authentication was succesful
- (CMISRequest*)authenticateWithCompletionBlock:(void (^)(CMISSession *session, NSError * error))completionBlock;
@end
@interface CMISSession (PrivateMethods)
- (BOOL)authenticateAndReturnError:(NSError **)error;
@end
@implementation CMISSession
#pragma mark -
#pragma mark Setup
+ (CMISRequest*)arrayOfRepositories:(CMISSessionParameters *)sessionParameters completionBlock:(void (^)(NSArray *repositories, NSError *error))completionBlock
{
CMISSession *session = [[CMISSession alloc] initWithSessionParameters:sessionParameters];
// TODO: validate session parameters?
// return list of repositories
return [session.binding.repositoryService retrieveRepositoriesWithCompletionBlock:completionBlock];
}
+ (CMISRequest*)connectWithSessionParameters:(CMISSessionParameters *)sessionParameters
completionBlock:(void (^)(CMISSession *session, NSError * error))completionBlock
{
CMISSession *session = [[CMISSession alloc] initWithSessionParameters:sessionParameters];
if (session) {
return [session authenticateWithCompletionBlock:completionBlock];
} else {
completionBlock(nil, [CMISErrors createCMISErrorWithCode:kCMISErrorCodeInvalidArgument
detailedDescription:@"Not enough session parameters to connect"]);
return nil;
}
}
#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) {
CMISLogError(@"No username or password provided for standard authentication provider");
return nil;
}
self.sessionParameters.authenticationProvider = [[CMISStandardAuthenticationProvider alloc] initWithUsername:username
password: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;
CMISLogDebug(@"Using a custom object converter class: %@", objectConverterClassName);
self.objectConverter = [[NSClassFromString(objectConverterClassName) alloc] initWithSession:self];
} else { //default
self.objectConverter = [[CMISObjectConverter alloc] initWithSession:self];
}
self.typeCache = [[NSMutableDictionary alloc] init];
// TODO: setup locale
// TODO: setup default session parameters
// TODO: setup caches
}
return self;
}
- (CMISRequest*)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
detailedDescription:@"Must provide repository id"];
CMISLogError(@"Error: %@", error.description);
completionBlock(nil, error);
return nil;
}
if (self.sessionParameters.authenticationProvider == nil) {
NSError *error = [CMISErrors createCMISErrorWithCode:kCMISErrorCodeUnauthorized detailedDescription:@"Must provide authentication provider"];
CMISLogError(@"Error: %@", error.description);
completionBlock(nil, error);
return nil;
}
// 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
return [self.binding.repositoryService retrieveRepositoryInfoForId:self.sessionParameters.repositoryId completionBlock:^(CMISRepositoryInfo *repositoryInfo, NSError *error) {
self.repositoryInfo = repositoryInfo;
if (self.repositoryInfo == nil) {
if (error) {
CMISLogError(@"Error because repositoryInfo is nil: %@", error.description);
completionBlock(nil, [CMISErrors cmisError:error cmisErrorCode:kCMISErrorCodeInvalidArgument]);
} else {
completionBlock(nil, [CMISErrors createCMISErrorWithCode:kCMISErrorCodeInvalidArgument
detailedDescription:@"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
- (CMISRequest*)retrieveRootFolderWithCompletionBlock:(void (^)(CMISFolder *folder, NSError *error))completionBlock
{
return [self retrieveFolderWithOperationContext:[CMISOperationContext defaultOperationContext] completionBlock:completionBlock];
}
- (CMISRequest*)retrieveFolderWithOperationContext:(CMISOperationContext *)operationContext completionBlock:(void (^)(CMISFolder *folder, NSError *error))completionBlock
{
NSString *rootFolderId = self.repositoryInfo.rootFolderId;
return [self retrieveObject:rootFolderId operationContext:operationContext completionBlock:^(CMISObject *rootFolder, NSError *error) {
if (rootFolder != nil && ![rootFolder isKindOfClass:[CMISFolder class]]) {
completionBlock(nil, [CMISErrors createCMISErrorWithCode:kCMISErrorCodeRuntime detailedDescription:@"Root folder object is not a folder!"]);
} else {
completionBlock((CMISFolder *)rootFolder, error);
}
}];
}
- (CMISRequest*)retrieveObject:(NSString *)objectId completionBlock:(void (^)(CMISObject *object, NSError *error))completionBlock
{
return [self retrieveObject:objectId operationContext:[CMISOperationContext defaultOperationContext] completionBlock:completionBlock];
}
- (CMISRequest*)retrieveObject:(NSString *)objectId
operationContext:(CMISOperationContext *)operationContext
completionBlock:(void (^)(CMISObject *object, NSError *error))completionBlock
{
if (objectId == nil) {
completionBlock(nil, [CMISErrors createCMISErrorWithCode:kCMISErrorCodeInvalidArgument detailedDescription:@"Must provide object id"]);
return nil;
}
// TODO: cache the object
return [self.binding.objectService retrieveObject:objectId
filter:operationContext.filterString
relationships:operationContext.relationships
includePolicyIds:operationContext.includePolicies
renditionFilder:operationContext.renditionFilterString
includeACL:operationContext.includeACLs
includeAllowableActions:operationContext.includeAllowableActions
completionBlock:^(CMISObjectData *objectData, NSError *error) {
if (error) {
completionBlock(nil, [CMISErrors cmisError:error cmisErrorCode:kCMISErrorCodeObjectNotFound]);
} else {
CMISObject *object = nil;
if (objectData) {
[self.objectConverter convertObject:objectData
completionBlock:^(CMISObject *object, NSError *error) {
completionBlock(object, error);
}];
} else {
completionBlock(object, nil);
}
}
}];
}
- (CMISRequest*)retrieveObjectByPath:(NSString *)path completionBlock:(void (^)(CMISObject *object, NSError *error))completionBlock
{
return [self retrieveObjectByPath:path operationContext:[CMISOperationContext defaultOperationContext] completionBlock:completionBlock];
}
- (CMISRequest*)retrieveObjectByPath:(NSString *)path
operationContext:(CMISOperationContext *)operationContext
completionBlock:(void (^)(CMISObject *object, NSError *error))completionBlock
{
return [self.binding.objectService retrieveObjectByPath:path
filter:operationContext.filterString
relationships:operationContext.relationships
includePolicyIds:operationContext.includePolicies
renditionFilder:operationContext.renditionFilterString
includeACL:operationContext.includeACLs
includeAllowableActions:operationContext.includeAllowableActions
completionBlock:^(CMISObjectData *objectData, NSError *error) {
if (objectData != nil && error == nil) {
[self.objectConverter convertObject:objectData
completionBlock:^(CMISObject *object, NSError *error) {
completionBlock(object, error);
}];
} else {
if (error == nil) {
NSError *error = [[NSError alloc] init];
error = [CMISErrors cmisError:error cmisErrorCode:kCMISErrorCodeObjectNotFound];
}
completionBlock(nil, error);
}
}];
}
- (CMISRequest*)retrieveTypeDefinition:(NSString *)typeId completionBlock:(void (^)(CMISTypeDefinition *typeDefinition, NSError *error))completionBlock
{
CMISTypeDefinition *typeDefinition = [self.typeCache objectForKey:typeId];
if (typeDefinition) {
completionBlock(typeDefinition, nil);
return nil;
}
return [self.binding.repositoryService retrieveTypeDefinition:typeId completionBlock:^(CMISTypeDefinition *typeDefinition, NSError *error) {
if (typeDefinition) {
[self.typeCache setObject:typeDefinition forKey:typeId];
}
completionBlock(typeDefinition, error);
}];
}
- (CMISRequest*)query:(NSString *)statement searchAllVersions:(BOOL)searchAllVersion completionBlock:(void (^)(CMISPagedResult *pagedResult, NSError *error))completionBlock
{
return [self query:statement searchAllVersions:searchAllVersion operationContext:[CMISOperationContext defaultOperationContext] completionBlock:completionBlock];
}
- (CMISRequest*)query:(NSString *)statement searchAllVersions:(BOOL)searchAllVersion
operationContext:(CMISOperationContext *)operationContext
completionBlock:(void (^)(CMISPagedResult *pagedResult, NSError *error))completionBlock
{
__block CMISRequest *request = [[CMISRequest alloc] init];
CMISFetchNextPageBlock fetchNextPageBlock = ^(int skipCount, int maxItems, CMISFetchNextPageBlockCompletionBlock pageBlockCompletionBlock){
// Fetch results through discovery service
request = [self.binding.discoveryService query:statement
searchAllVersions:searchAllVersion
relationships:operationContext.relationships
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 cmisErrorCode: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 session:self]];
}
pageBlockCompletionBlock(result, nil);
}
}];
};
[CMISPagedResult pagedResultUsingFetchBlock:fetchNextPageBlock
limitToMaxItems:operationContext.maxItemsPerPage
startFromSkipCount:operationContext.skipCount
completionBlock:^(CMISPagedResult *result, NSError *error) {
// Return nil and populate error in case something went wrong
if (error) {
completionBlock(nil, [CMISErrors cmisError:error cmisErrorCode:kCMISErrorCodeRuntime]);
} else {
completionBlock(result, nil);
}
}];
return request;
}
- (CMISRequest*)queryObjectsWithTypeDefinition:(CMISTypeDefinition *)typeDefinition
whereClause:(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];
}
__block CMISRequest *request = [[CMISRequest alloc] init];
// Fetch block for paged results
CMISFetchNextPageBlock fetchNextPageBlock = ^(int skipCount, int maxItems, CMISFetchNextPageBlockCompletionBlock pageBlockCompletionBlock)
{
// Fetch results through discovery service
request = [self.binding.discoveryService query:statement
searchAllVersions:searchAllVersion
relationships:operationContext.relationships
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 cmisErrorCode:kCMISErrorCodeRuntime]);
} else {
// Fill up return result
CMISFetchNextPageBlockResult *result = [[CMISFetchNextPageBlockResult alloc] init];
result.hasMoreItems = objectList.hasMoreItems;
result.numItems = objectList.numItems;
[self.objectConverter convertObjects:objectList.objects
completionBlock:^(NSArray *objects, NSError *error) {
result.resultArray = objects;
pageBlockCompletionBlock(result, error);
}];
}
}];
};
[CMISPagedResult pagedResultUsingFetchBlock:fetchNextPageBlock
limitToMaxItems:operationContext.maxItemsPerPage
startFromSkipCount:operationContext.skipCount
completionBlock:^(CMISPagedResult *result, NSError *error) {
// Return nil and populate error in case something went wrong
if (error) {
completionBlock(nil, [CMISErrors cmisError:error cmisErrorCode:kCMISErrorCodeRuntime]);
} else {
completionBlock(result, nil);
}
}];
return request;
}
- (CMISRequest*)queryObjectsWithTypeid:(NSString *)typeId
whereClause:(NSString *)whereClause
searchAllVersions:(BOOL)searchAllVersion
operationContext:(CMISOperationContext *)operationContext
completionBlock:(void (^)(CMISPagedResult *result, NSError *error))completionBlock
{
return [self retrieveTypeDefinition:typeId
completionBlock:^(CMISTypeDefinition *typeDefinition, NSError *internalError) {
if (internalError != nil) {
NSError *error = [CMISErrors cmisError:internalError cmisErrorCode:kCMISErrorCodeRuntime];
completionBlock(nil, error);
} else {
[self queryObjectsWithTypeDefinition:typeDefinition
whereClause:whereClause
searchAllVersions:searchAllVersion
operationContext:operationContext
completionBlock:completionBlock];
}
}];
}
- (CMISRequest*)createFolder:(NSDictionary *)properties
inFolder:(NSString *)folderObjectId
completionBlock:(void (^)(NSString *objectId, NSError *error))completionBlock
{
__block CMISRequest *request = [[CMISRequest alloc] init];
[self.objectConverter convertProperties:properties
forObjectTypeId:[properties objectForKey:kCMISPropertyObjectTypeId]
completionBlock:^(CMISProperties *convertedProperties, NSError *error) {
if (error) {
completionBlock(nil, [CMISErrors cmisError:error cmisErrorCode:kCMISErrorCodeRuntime]);
} else {
request = [self.binding.objectService createFolderInParentFolder:folderObjectId
properties:convertedProperties
completionBlock:^(NSString *objectId, NSError *error) {
completionBlock(objectId, error);
}];
}
}];
return request;
}
- (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
streamId: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
streamId:nil
toOutputStream:outputStream
completionBlock:completionBlock
progressBlock:progressBlock];
}
- (CMISRequest*)createDocumentFromFilePath:(NSString *)filePath
mimeType:(NSString *)mimeType
properties:(NSDictionary *)properties inFolder:(NSString *)folderObjectId
completionBlock:(void (^)(NSString *objectId, NSError *error))completionBlock
progressBlock:(void (^)(unsigned long long bytesUploaded, unsigned long long bytesTotal))progressBlock
{
__block CMISRequest *request = [[CMISRequest alloc] init];
[self.objectConverter convertProperties:properties
forObjectTypeId:[properties objectForKey:kCMISPropertyObjectTypeId]
completionBlock:^(CMISProperties *convertedProperties, NSError *error) {
if (error) {
CMISLogError(@"Could not convert properties: %@", error.description);
if (completionBlock) {
completionBlock(nil, [CMISErrors cmisError:error cmisErrorCode:kCMISErrorCodeRuntime]);
}
} else {
request = [self.binding.objectService createDocumentFromFilePath:filePath
mimeType:mimeType
properties:convertedProperties
inFolder:folderObjectId
completionBlock:completionBlock
progressBlock:progressBlock];
}
}];
return request;
}
- (CMISRequest*)createDocumentFromInputStream:(NSInputStream *)inputStream
mimeType:(NSString *)mimeType
properties:(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
{
__block CMISRequest *request = [[CMISRequest alloc] init];
[self.objectConverter convertProperties:properties
forObjectTypeId:[properties objectForKey:kCMISPropertyObjectTypeId]
completionBlock:^(CMISProperties *convertedProperties, NSError *error) {
if (error) {
CMISLogError(@"Could not convert properties: %@", error.description);
if (completionBlock) {
completionBlock(nil, [CMISErrors cmisError:error cmisErrorCode:kCMISErrorCodeRuntime]);
}
} else {
request = [self.binding.objectService createDocumentFromInputStream:inputStream
mimeType:mimeType
properties:convertedProperties
inFolder:folderObjectId
bytesExpected:bytesExpected
completionBlock:completionBlock
progressBlock:progressBlock];
}
}];
return request;
}
@end