X-Git-Url: https://code.grnet.gr/git/pithos-macos/blobdiff_plain/1b1e6fa121f3621ae3e322d47d49c63333fb8f43..6d9d5dce6b6df63e7e44314f25ee2a39b953397d:/pithos-macos/PithosObjectNode.m diff --git a/pithos-macos/PithosObjectNode.m b/pithos-macos/PithosObjectNode.m index 2c1919f..ffe8fd1 100644 --- a/pithos-macos/PithosObjectNode.m +++ b/pithos-macos/PithosObjectNode.m @@ -2,7 +2,7 @@ // PithosObjectNode.m // pithos-macos // -// Copyright 2011 GRNET S.A. All rights reserved. +// Copyright 2011-2013 GRNET S.A. All rights reserved. // // Redistribution and use in source and binary forms, with or // without modification, are permitted provided that the following @@ -37,50 +37,94 @@ #import "PithosObjectNode.h" #import "ASIPithosRequest.h" +#import "ASIPithos.h" #import "ASIPithosObjectRequest.h" #import "ASIPithosContainer.h" #import "ASIPithosObject.h" +#import "ASIPithosSharingUser.h" #import "ASIDownloadCache.h" +#import "PithosAccount.h" #import "PithosUtilities.h" #import "PithosObjectNodeInfoController.h" @implementation PithosObjectNode -@synthesize pithosContainer, pithosObject; -@synthesize isPublic; +@synthesize pithosContainer, pithosObject, versions, applyMetadataObjectRequest, refreshMetadataObjectRequest, refreshVersionsObjectRequest; +@synthesize isPublic, translatedModifiedBy, translatedPermissions; #pragma mark - #pragma mark Object Lifecycle -- (id)initWithPithosContainer:(ASIPithosContainer *)aPithosContainer pithosObject:(ASIPithosObject *)aPithosObject { - if ((self = [super init])) { +- (id)initWithPithosAccountManager:(PithosAccount *)aPithosAccountManager + pithosContainer:(ASIPithosContainer *)aPithosContainer + pithosObject:(ASIPithosObject *)aPithosObject { + if ((self = [super initWithPithosAccountManager:aPithosAccountManager])) { + isLeafItem = YES; self.pithosContainer = aPithosContainer; self.pithosObject = aPithosObject; - isLeafItem = YES; } return self; } - (void)dealloc { + [refreshVersionsObjectRequest clearDelegatesAndCancel]; [refreshMetadataObjectRequest clearDelegatesAndCancel]; - [refreshMetadataObjectRequest release]; [applyMetadataObjectRequest clearDelegatesAndCancel]; - [applyMetadataObjectRequest release]; - [pithosObject release]; - [pithosContainer release]; - [super dealloc]; +} + +#pragma mark - +#pragma mark Internal + +- (void)updateModifiedBy { + if (!pithosObject.modifiedBy) { + self.translatedModifiedBy = nil; + } else if (pithosAccountManager) { + NSString *displayname = [pithosAccountManager displaynameForUUID:pithosObject.modifiedBy safe:NO]; + if (displayname) { + self.translatedModifiedBy = displayname; + } else { + [pithosAccountManager updateUserCatalogForDisplaynames:nil UUIDs:[NSArray arrayWithObject:pithosObject.modifiedBy]]; + self.translatedModifiedBy = [pithosAccountManager displaynameForUUID:pithosObject.modifiedBy safe:YES]; + } + } else { + self.translatedModifiedBy = [pithosObject.modifiedBy copy]; + } +} + +- (void)updatePermissions { + if (!pithosObject) { + self.translatedPermissions = [NSMutableArray array]; + } else if (pithosAccountManager) { + NSMutableSet *UUIDs = [NSMutableSet set]; + for (ASIPithosSharingUser *sharingUser in pithosObject.permissions) { + [UUIDs addObject:sharingUser.name]; + } + [UUIDs removeObject:@""]; + [UUIDs removeObject:@"*"]; + if (UUIDs.count) { + [pithosAccountManager updateUserCatalogForDisplaynames:nil UUIDs:[UUIDs allObjects]]; + } + + NSMutableArray *newTranslatedPermissions = [NSMutableArray arrayWithCapacity:pithosObject.permissions.count]; + for (ASIPithosSharingUser *sharingUser in pithosObject.permissions) { + ASIPithosSharingUser *translatedSharingUser = [sharingUser copy]; + translatedSharingUser.name = [pithosAccountManager displaynameForUUID:translatedSharingUser.name safe:YES]; + [newTranslatedPermissions addObject:translatedSharingUser]; + } + self.translatedPermissions = newTranslatedPermissions; + } else { + self.translatedPermissions = [NSMutableArray arrayWithArray:[pithosObject.permissions copy]]; + } } #pragma mark - #pragma mark Properties - (NSString *)url { - if (url == nil) - url = [[NSString alloc] initWithFormat:@"object %@/%@/%@%@", - (sharingAccount ? [ASIPithosRequest storageURLWithAuthUser:sharingAccount] : [ASIPithosRequest storageURL]), - pithosContainer.name, - pithosObject.name, - (shared ? @"?shared" : @"")]; - return url; + return [NSString stringWithFormat:@"@object@%@/%@/%@%@", + (sharingAccount ? sharingAccount : pithosAccountManager.pithos.authUser), + pithosContainer.name, + pithosObject.name, + (shared ? @"?shared" : @"")]; } - (NSArray *)children { @@ -92,9 +136,8 @@ displayName = [pithosObject.name lastPathComponent]; if([pithosObject.name hasSuffix:@"/"]) displayName = [displayName stringByAppendingString:@"/"]; - [displayName retain]; } - return [[displayName copy] autorelease]; + return [displayName copy]; } - (void)setDisplayName:(NSString *)aDisplayName { @@ -102,60 +145,119 @@ - (NSImage *)icon { if (icon == nil) - icon = [[[NSWorkspace sharedWorkspace] iconForFileType:[pithosObject.name pathExtension]] retain]; + icon = [[NSWorkspace sharedWorkspace] iconForFileType:[pithosObject.name pathExtension]]; return icon; } - (void)setPithosObject:(ASIPithosObject *)aPithosObject { if (![pithosObject isEqualTo:aPithosObject]) { - [pithosObject release]; - pithosObject = [aPithosObject retain]; + pithosObject = aPithosObject; + [self updateModifiedBy]; + [self updatePermissions]; } self.isPublic = (pithosObject.publicURI != nil); // Refresh browser if the object is in my shared and is no longer shared if (shared && !pithosObject.sharing) - [[NSNotificationCenter defaultCenter] postNotificationName:@"PithosBrowserRefreshNeeeded" object:self]; + [[NSNotificationCenter defaultCenter] postNotificationName:@"PithosBrowserRefreshNeeded" object:self]; +} + +- (void)setLimitedPithosObject:(ASIPithosObject *)aPithosObject { + if (![pithosObject isEqualTo:aPithosObject]) { + self.pithosObject.subdir = aPithosObject.subdir; + self.pithosObject.name = aPithosObject.name; + self.pithosObject.hash = aPithosObject.hash; + self.pithosObject.objectHash = aPithosObject.objectHash; + self.pithosObject.UUID = aPithosObject.UUID; + self.pithosObject.bytes = aPithosObject.bytes; + self.pithosObject.contentType = aPithosObject.contentType; + self.pithosObject.lastModified = aPithosObject.lastModified; + self.pithosObject.version = aPithosObject.version; + self.pithosObject.versionTimestamp = aPithosObject.versionTimestamp; + self.pithosObject.modifiedBy = aPithosObject.modifiedBy; + self.pithosObject.sharedBy = aPithosObject.sharedBy; + self.pithosObject.allowedTo = aPithosObject.allowedTo; + if (!pithosNodeInfoController) { + self.pithosObject.sharing = aPithosObject.sharing; + self.pithosObject.publicURI = aPithosObject.publicURI; + self.pithosObject = pithosObject; + [self updatePermissions]; + } else { + [self updateModifiedBy]; + } + } } #pragma mark - #pragma mark ASIHTTPRequestDelegate - (void)objectRequestFinished:(ASIPithosObjectRequest *)request { - NSLog(@"URL: %@", [request url]); - NSLog(@"cached: %d", [request didUseCachedResponse]); - - if ([request isEqualTo:applyMetadataObjectRequest]) { - int responseStatusCode = applyMetadataObjectRequest.responseStatusCode; - if (responseStatusCode != 202) - [PithosUtilities unexpectedResponseStatusAlertWithRequest:applyMetadataObjectRequest]; - @synchronized(self) { - [applyMetadataObjectRequest release]; - applyMetadataObjectRequest = nil; - } - if (responseStatusCode == 202) - [self refreshInfo]; - } else if ([request isEqualTo:refreshMetadataObjectRequest]) { - [[pithosNodeInfoController window] makeFirstResponder:nil]; - self.pithosObject = [refreshMetadataObjectRequest object]; - @synchronized(self) { - [refreshMetadataObjectRequest release]; - refreshMetadataObjectRequest = nil; + @autoreleasepool { + DLog(@"URL: %@", [request url]); + DLog(@"cached: %d", [request didUseCachedResponse]); + + if ([request isEqualTo:applyMetadataObjectRequest]) { + int responseStatusCode = applyMetadataObjectRequest.responseStatusCode; + if (responseStatusCode != 202) + [PithosUtilities unexpectedResponseStatusAlertWithRequest:applyMetadataObjectRequest]; + @synchronized(self) { + self.applyMetadataObjectRequest = nil; + } + if (responseStatusCode == 202) + [self refreshInfo]; + } else if ([request isEqualTo:refreshMetadataObjectRequest]) { + [[pithosNodeInfoController window] makeFirstResponder:nil]; + self.pithosObject = [refreshMetadataObjectRequest object]; + @synchronized(self) { + self.refreshMetadataObjectRequest = nil; + } + } else if ([request isEqualTo:refreshVersionsObjectRequest]) { + [[pithosNodeInfoController window] makeFirstResponder:nil]; + self.versions = [refreshVersionsObjectRequest versions]; + @synchronized(self) { + self.refreshVersionsObjectRequest = nil; + } } } } - (void)objectRequestFailed:(ASIPithosObjectRequest *)request { - if ([request isEqualTo:applyMetadataObjectRequest]) { - [PithosUtilities httpRequestErrorAlertWithRequest:applyMetadataObjectRequest]; - @synchronized(self) { - [applyMetadataObjectRequest release]; - applyMetadataObjectRequest = nil; - } - } else if ([request isEqualTo:refreshMetadataObjectRequest]) { - [PithosUtilities httpRequestErrorAlertWithRequest:refreshMetadataObjectRequest]; - @synchronized(self) { - [refreshMetadataObjectRequest release]; - refreshMetadataObjectRequest = nil; + @autoreleasepool { + NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue]; + if (retries > 0) { + ASIPithosObjectRequest *newRequest = (ASIPithosObjectRequest *)[PithosUtilities retryWithUpdatedURLRequest:request + andPithosAccountManager:pithosAccountManager]; + [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"]; + if ([request isEqualTo:applyMetadataObjectRequest]) { + @synchronized(self) { + self.applyMetadataObjectRequest = newRequest; + } + } else if ([request isEqualTo:refreshMetadataObjectRequest]) { + @synchronized(self) { + self.refreshMetadataObjectRequest = newRequest; + } + } else if ([request isEqualTo:refreshVersionsObjectRequest]) { + @synchronized(self) { + self.refreshVersionsObjectRequest = newRequest; + } + } + [[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous]; + } else { + if ([request isEqualTo:applyMetadataObjectRequest]) { + [PithosUtilities httpRequestErrorAlertWithRequest:applyMetadataObjectRequest]; + @synchronized(self) { + self.applyMetadataObjectRequest = nil; + } + } else if ([request isEqualTo:refreshMetadataObjectRequest]) { + [PithosUtilities httpRequestErrorAlertWithRequest:refreshMetadataObjectRequest]; + @synchronized(self) { + self.refreshMetadataObjectRequest = nil; + } + } else if ([request isEqualTo:refreshVersionsObjectRequest]) { + [PithosUtilities httpRequestErrorAlertWithRequest:refreshVersionsObjectRequest]; + @synchronized(self) { + self.refreshVersionsObjectRequest = nil; + } + } } } } @@ -168,30 +270,156 @@ if (applyMetadataObjectRequest == nil) { [[pithosNodeInfoController window] makeFirstResponder:nil]; if (sharingAccount) { - applyMetadataObjectRequest = [[ASIPithosObjectRequest updateObjectMetadataRequestWithContainerName:pithosContainer.name - objectName:pithosObject.name - contentEncoding:nil - contentDisposition:nil - manifest:nil - sharing:nil - isPublic:(isPublic ? ASIPithosObjectRequestPublicTrue : ASIPithosObjectRequestPublicFalse) - metadata:pithosObject.metadata - update:NO] retain]; - [applyMetadataObjectRequest setRequestUserFromDefaultTo:sharingAccount]; + self.applyMetadataObjectRequest = [ASIPithosObjectRequest updateObjectMetadataRequestWithPithos:pithosAccountManager.pithos + containerName:pithosContainer.name + objectName:pithosObject.name + contentEncoding:nil + contentDisposition:nil + manifest:nil + sharing:nil + isPublic:(isPublic ? ASIPithosObjectRequestPublicTrue : ASIPithosObjectRequestPublicFalse) + metadata:pithosObject.metadata + update:NO]; + [applyMetadataObjectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithosAccountManager.pithos]; } else { - applyMetadataObjectRequest = [[ASIPithosObjectRequest updateObjectMetadataRequestWithContainerName:pithosContainer.name - objectName:pithosObject.name - contentEncoding:pithosObject.contentEncoding - contentDisposition:pithosObject.contentDisposition - manifest:pithosObject.manifest - sharing:(pithosObject.sharing ? pithosObject.sharing : @"") - isPublic:(isPublic ? ASIPithosObjectRequestPublicTrue : ASIPithosObjectRequestPublicFalse) - metadata:pithosObject.metadata - update:NO] retain]; + NSMutableArray *permissions = [NSMutableArray array]; + if (translatedPermissions.count) { + for (ASIPithosSharingUser *translatedsSharingUser in translatedPermissions) { + if (translatedsSharingUser.group.length && + [translatedsSharingUser.group rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@" -_~,;"]].location != NSNotFound) { + NSAlert *alert = [[NSAlert alloc] init]; + [alert setMessageText:@"Invalid Input"]; + [alert setInformativeText:@"Group names cannot contain ' ', '-', '_', '~', ',' or ';'."]; + [alert addButtonWithTitle:@"OK"]; + [alert runModal]; + return; + } + } + if (pithosAccountManager) { + NSMutableSet *allUsers = [NSMutableSet set]; + for (ASIPithosSharingUser *translatedsSharingUser in translatedPermissions) { + if (translatedsSharingUser.name.length) { + [allUsers addObject:translatedsSharingUser.name]; + } + } + [allUsers removeObject:@""]; + [allUsers removeObject:@"*"]; + if (allUsers.count) { + ASIPithosRequest *userCatalogRequest = [pithosAccountManager updateUserCatalogForDisplaynames:[allUsers allObjects] + UUIDs:nil]; + if (userCatalogRequest.error || ((userCatalogRequest.responseStatusCode != 200) && (userCatalogRequest.responseStatusCode != 404))) { + return; + } else if (userCatalogRequest.responseStatusCode == 200) { + // Check if all users exist. + NSDictionary *displaynameCatalog = [userCatalogRequest displaynameCatalog]; + NSMutableArray *inexistentUsers = [NSMutableArray array]; + for (NSString *user in allUsers) { + if (![displaynameCatalog objectForKey:user]) { + [inexistentUsers addObject:user]; + } + } + if (!inexistentUsers.count) { + // Create permissions. + for (ASIPithosSharingUser *translatedsSharingUser in translatedPermissions) { + if (translatedsSharingUser.name.length) { + ASIPithosSharingUser *sharingUser = [translatedsSharingUser copy]; + if (![sharingUser.name isEqualToString:@"*"]) { + sharingUser.name = [displaynameCatalog objectForKey:sharingUser.name]; + } + if (!sharingUser.permission) { + sharingUser.permission = @"read"; + } + [permissions addObject:sharingUser]; + } + } + } else { + NSAlert *alert = [[NSAlert alloc] init]; + if (inexistentUsers.count == 1) { + [alert setMessageText:@"Invalid User"]; + [alert setInformativeText:[NSString stringWithFormat:@"User '%@' doesn't exist.", [inexistentUsers objectAtIndex:0]]]; + } else { + [alert setMessageText:@"Invalid Users"]; + [alert setInformativeText:[NSString stringWithFormat:@"Users '%@' don't exist.", [inexistentUsers componentsJoinedByString:@"', '"]]]; + } + [alert addButtonWithTitle:@"OK"]; + [alert runModal]; + return; + } + } else { + // 404. Since we don't translate to UUIDs, check for invalid chars. + BOOL valid = YES; + // Create permissions. + for (ASIPithosSharingUser *translatedsSharingUser in translatedPermissions) { + if (translatedsSharingUser.name.length && + ([translatedsSharingUser.name rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@" ~,;:"]].location != NSNotFound)) { + valid = NO; + break; + } + } + if (valid) { + for (ASIPithosSharingUser *translatedsSharingUser in translatedPermissions) { + if (translatedsSharingUser.name.length) { + ASIPithosSharingUser *sharingUser = [translatedsSharingUser copy]; + if (!sharingUser.permission) { + sharingUser.permission = @"read"; + } + [permissions addObject:sharingUser]; + } + } + } else { + NSAlert *alert = [[NSAlert alloc] init]; + [alert setMessageText:@"Invalid Input"]; + [alert setInformativeText:@"Users cannot contain ' ', '~', ',', ';' or ':'."]; + [alert addButtonWithTitle:@"OK"]; + [alert runModal]; + return; + } + } + } else { + for (ASIPithosSharingUser *translatedsSharingUser in translatedPermissions) { + if ([translatedsSharingUser.name isEqualToString:@"*"]) { + ASIPithosSharingUser *sharingUser = [translatedsSharingUser copy]; + if (!sharingUser.permission) { + sharingUser.permission = @"read"; + } + [permissions addObject:sharingUser]; + } + } + } + } else { + for (ASIPithosSharingUser *translatedsSharingUser in translatedPermissions) { + if (translatedsSharingUser.name.length) { + ASIPithosSharingUser *sharingUser = [translatedsSharingUser copy]; + if (!sharingUser.permission) { + sharingUser.permission = @"read"; + } + [permissions addObject:sharingUser]; + } + } + } + } + pithosObject.permissions = permissions; + + self.applyMetadataObjectRequest = [ASIPithosObjectRequest updateObjectMetadataRequestWithPithos:pithosAccountManager.pithos + containerName:pithosContainer.name + objectName:pithosObject.name + contentEncoding:pithosObject.contentEncoding + contentDisposition:pithosObject.contentDisposition + manifest:pithosObject.manifest + sharing:(pithosObject.sharing ? pithosObject.sharing : @"") + isPublic:(isPublic ? ASIPithosObjectRequestPublicTrue : ASIPithosObjectRequestPublicFalse) + metadata:pithosObject.metadata + update:NO]; } applyMetadataObjectRequest.delegate = self; - applyMetadataObjectRequest.didFinishSelector = @selector(objectRequestFinished:); - applyMetadataObjectRequest.didFailSelector = @selector(objectRequestFailed:); + applyMetadataObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + applyMetadataObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + applyMetadataObjectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", + [NSNumber numberWithUnsignedInteger:10], @"retries", + NSStringFromSelector(@selector(objectRequestFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(objectRequestFailed:)), @"didFailSelector", + nil]; [[PithosUtilities prepareRequest:applyMetadataObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous]; } } @@ -200,25 +428,63 @@ - (void)refreshInfo { @synchronized(self) { if (refreshMetadataObjectRequest == nil) { - refreshMetadataObjectRequest = [[ASIPithosObjectRequest objectMetadataRequestWithContainerName:pithosContainer.name - objectName:pithosObject.name] retain]; + self.refreshMetadataObjectRequest = [ASIPithosObjectRequest objectMetadataRequestWithPithos:pithosAccountManager.pithos + containerName:pithosContainer.name + objectName:pithosObject.name]; if (sharingAccount) - [refreshMetadataObjectRequest setRequestUserFromDefaultTo:sharingAccount]; + [refreshMetadataObjectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithosAccountManager.pithos]; refreshMetadataObjectRequest.delegate = self; - refreshMetadataObjectRequest.didFinishSelector = @selector(objectRequestFinished:); - refreshMetadataObjectRequest.didFailSelector = @selector(objectRequestFailed:); - refreshMetadataObjectRequest.downloadCache = [ASIDownloadCache sharedCache]; + refreshMetadataObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + refreshMetadataObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + refreshMetadataObjectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", + [NSNumber numberWithUnsignedInteger:10], @"retries", + NSStringFromSelector(@selector(objectRequestFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(objectRequestFailed:)), @"didFailSelector", + nil]; + if (!sharingAccount) + refreshMetadataObjectRequest.downloadCache = [ASIDownloadCache sharedCache]; [[PithosUtilities prepareRequest:refreshMetadataObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous]; } } + [self refreshVersions]; +} + +#pragma mark - +#pragma mark Versions + +- (void)refreshVersions { + @synchronized(self) { + if (refreshVersionsObjectRequest == nil) { + self.refreshVersionsObjectRequest = [ASIPithosObjectRequest objectVersionsRequestWithPithos:pithosAccountManager.pithos + containerName:pithosContainer.name + objectName:pithosObject.name]; + if (sharingAccount) + [refreshVersionsObjectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithosAccountManager.pithos]; + refreshVersionsObjectRequest.delegate = self; + refreshVersionsObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:); + refreshVersionsObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:); + refreshVersionsObjectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", + [NSNumber numberWithUnsignedInteger:10], @"retries", + NSStringFromSelector(@selector(objectRequestFinished:)), @"didFinishSelector", + NSStringFromSelector(@selector(objectRequestFailed:)), @"didFailSelector", + nil]; + if (!sharingAccount) + refreshVersionsObjectRequest.downloadCache = [ASIDownloadCache sharedCache]; + [[PithosUtilities prepareRequest:refreshVersionsObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous]; + } + } } #pragma mark - #pragma mark Actions - (void)showPithosNodeInfo:(id)sender { - if (!pithosNodeInfoController) + if (!pithosNodeInfoController) { pithosNodeInfoController = [[PithosObjectNodeInfoController alloc] initWithPithosNode:self]; + [self refreshInfo]; + } [pithosNodeInfoController showWindow:sender]; [[pithosNodeInfoController window] makeKeyAndOrderFront:sender]; [NSApp activateIgnoringOtherApps:YES];