5 // Copyright 2011-2012 GRNET S.A. All rights reserved.
7 // Redistribution and use in source and binary forms, with or
8 // without modification, are permitted provided that the following
11 // 1. Redistributions of source code must retain the above
12 // copyright notice, this list of conditions and the following
15 // 2. Redistributions in binary form must reproduce the above
16 // copyright notice, this list of conditions and the following
17 // disclaimer in the documentation and/or other materials
18 // provided with the distribution.
20 // THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
21 // OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
24 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
27 // USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28 // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 // POSSIBILITY OF SUCH DAMAGE.
33 // The views and conclusions contained in the software and
34 // documentation are those of the authors and should not be
35 // interpreted as representing official policies, either expressed
36 // or implied, of GRNET S.A.
38 #import "PithosSubdirNode.h"
39 #import "ASIPithosRequest.h"
41 #import "ASIPithosObjectRequest.h"
42 #import "ASIPithosContainer.h"
43 #import "ASIPithosObject.h"
44 #import "ASINetworkQueue.h"
45 #import "ASIDownloadCache.h"
46 #import "PithosUtilities.h"
47 #import "PithosObjectNodeInfoController.h"
49 static NSImage *sharedIcon = nil;
51 @implementation PithosSubdirNode
52 @synthesize pithosObject, versions;
56 if (self == [PithosSubdirNode class])
57 sharedIcon = [[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericFolderIcon)] retain];
61 #pragma mark Object Lifecycle
63 - (id)initWithPithos:(ASIPithos *)aPithos
64 pithosContainer:(ASIPithosContainer *)aPithosContainer
65 pithosObject:(ASIPithosObject *)aPithosObject {
66 if ((self = [super init])) {
67 self.pithos = aPithos;
68 self.pithosContainer = aPithosContainer;
69 self.pithosObject = aPithosObject;
70 childrenUpdatedNotificationName = @"PithosSubdirNodeChildrenUpdated";
77 [refreshVersionsObjectRequest clearDelegatesAndCancel];
78 [refreshVersionsObjectRequest release];
79 [refreshMetadataObjectRequest clearDelegatesAndCancel];
80 [refreshMetadataObjectRequest release];
81 [applyMetadataObjectRequest clearDelegatesAndCancel];
82 [applyMetadataObjectRequest release];
84 [pithosObject release];
89 #pragma mark Properties
91 - (void)setPithos:(ASIPithos *)aPithos {
92 if (aPithos && ![aPithos isEqualTo:pithos]) {
94 pithos = [aPithos retain];
102 url = [[NSString alloc] initWithFormat:@"subdir %@/%@/%@%@",
103 (sharingAccount ? [pithos storageURLWithAuthUser:sharingAccount] : pithos.storageURL),
104 pithosContainer.name,
106 (shared ? @"?shared" : @"")];
110 - (NSString *)displayName {
111 if (displayName == nil) {
112 displayName = [pithosObject.name lastPathComponent];
113 if (!pithosObject.subdir && [pithosObject.name hasSuffix:@"/"])
114 displayName = [displayName stringByAppendingString:@"/"];
115 [displayName retain];
117 return [[displayName copy] autorelease];
120 - (void)setDisplayName:(NSString *)aDisplayName {
125 icon = [sharedIcon retain];
129 - (void)setPithosObject:(ASIPithosObject *)aPithosObject {
130 if (![pithosObject isEqualTo:aPithosObject]) {
131 [pithosObject release];
132 pithosObject = [aPithosObject retain];
134 if (pithosObject.subdir) {
135 self.prefix = [pithosObject.name substringToIndex:([pithosObject.name length] - 1)];
137 self.prefix = [NSString stringWithString:pithosObject.name];
139 self.isPublic = (pithosObject.publicURI != nil);
140 // Refresh browser if the object is in my shared and is no longer shared
141 if (shared && !pithosObject.subdir && !pithosObject.sharing) {
142 [[NSNotificationCenter defaultCenter] postNotificationName:@"PithosBrowserRefreshNeeeded" object:self];
146 - (void)setLimitedPithosObject:(ASIPithosObject *)aPithosObject {
147 if (![pithosObject isEqualTo:aPithosObject]) {
148 self.pithosObject.subdir = aPithosObject.subdir;
149 self.pithosObject.name = aPithosObject.name;
150 self.pithosObject.hash = aPithosObject.hash;
151 self.pithosObject.objectHash = aPithosObject.objectHash;
152 self.pithosObject.UUID = aPithosObject.UUID;
153 self.pithosObject.bytes = aPithosObject.bytes;
154 self.pithosObject.contentType = aPithosObject.contentType;
155 self.pithosObject.lastModified = aPithosObject.lastModified;
156 self.pithosObject.version = aPithosObject.version;
157 self.pithosObject.versionTimestamp = aPithosObject.versionTimestamp;
158 self.pithosObject.modifiedBy = aPithosObject.modifiedBy;
159 self.pithosObject.sharedBy = aPithosObject.sharedBy;
160 self.pithosObject.allowedTo = aPithosObject.allowedTo;
161 if (!pithosNodeInfoController) {
162 self.pithosObject.sharing = aPithosObject.sharing;
163 self.pithosObject.publicURI = aPithosObject.publicURI;
164 self.pithosObject = pithosObject;
170 #pragma mark ASIHTTPRequestDelegate
172 - (void)objectRequestFinished:(ASIPithosObjectRequest *)request {
173 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
174 NSLog(@"URL: %@", [request url]);
175 NSLog(@"cached: %d", [request didUseCachedResponse]);
177 if ([request isEqualTo:applyMetadataObjectRequest]) {
178 int responseStatusCode = applyMetadataObjectRequest.responseStatusCode;
179 if ((responseStatusCode != 201) && (responseStatusCode != 202))
180 [PithosUtilities unexpectedResponseStatusAlertWithRequest:applyMetadataObjectRequest];
181 @synchronized(self) {
182 [applyMetadataObjectRequest release];
183 applyMetadataObjectRequest = nil;
185 if ((responseStatusCode == 201) || (responseStatusCode == 202))
187 } else if ([request isEqualTo:refreshMetadataObjectRequest]) {
188 [[pithosNodeInfoController window] makeFirstResponder:nil];
189 self.pithosObject = [refreshMetadataObjectRequest object];
191 // Ask the parent for refresh for the case where an object was removed
192 // It is done here so that it doesn't affect the info window refresh
196 @synchronized(self) {
197 [refreshMetadataObjectRequest release];
198 refreshMetadataObjectRequest = nil;
200 } else if ([request isEqualTo:refreshVersionsObjectRequest]) {
201 [[pithosNodeInfoController window] makeFirstResponder:nil];
202 self.versions = [refreshVersionsObjectRequest versions];
203 @synchronized(self) {
204 [refreshVersionsObjectRequest release];
205 refreshVersionsObjectRequest = nil;
211 - (void)objectRequestFailed:(ASIPithosObjectRequest *)request {
212 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
213 NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue];
215 ASIPithosObjectRequest *newRequest = (ASIPithosObjectRequest *)[[PithosUtilities copyRequest:request] autorelease];
216 [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
217 if ([request isEqualTo:applyMetadataObjectRequest]) {
218 @synchronized(self) {
219 [applyMetadataObjectRequest release];
220 applyMetadataObjectRequest = newRequest;
222 } else if ([request isEqualTo:refreshMetadataObjectRequest]) {
223 @synchronized(self) {
224 [refreshMetadataObjectRequest release];
225 refreshMetadataObjectRequest = newRequest;
227 } else if ([request isEqualTo:refreshVersionsObjectRequest]) {
228 @synchronized(self) {
229 [refreshVersionsObjectRequest release];
230 refreshVersionsObjectRequest = newRequest;
233 [[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous];
235 if ([request isEqualTo:applyMetadataObjectRequest]) {
236 dispatch_async(dispatch_get_main_queue(), ^{
237 [PithosUtilities httpRequestErrorAlertWithRequest:applyMetadataObjectRequest];
239 @synchronized(self) {
240 [applyMetadataObjectRequest release];
241 applyMetadataObjectRequest = nil;
243 } else if ([request isEqualTo:refreshMetadataObjectRequest]) {
244 dispatch_async(dispatch_get_main_queue(), ^{
245 [PithosUtilities httpRequestErrorAlertWithRequest:refreshMetadataObjectRequest];
247 @synchronized(self) {
248 [refreshMetadataObjectRequest release];
249 refreshMetadataObjectRequest = nil;
251 } else if ([request isEqualTo:refreshVersionsObjectRequest]) {
252 dispatch_async(dispatch_get_main_queue(), ^{
253 [PithosUtilities httpRequestErrorAlertWithRequest:refreshVersionsObjectRequest];
255 @synchronized(self) {
256 [refreshVersionsObjectRequest release];
257 refreshVersionsObjectRequest = nil;
268 @synchronized(self) {
269 if (applyMetadataObjectRequest == nil) {
270 if (pithosObject.subdir) {
271 BOOL createObject = NO;
273 ASIPithosObjectRequest *request = [ASIPithosObjectRequest objectMetadataRequestWithPithos:pithos
274 containerName:pithosContainer.name
276 ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
278 [networkQueue addOperations:[NSArray arrayWithObject:[PithosUtilities prepareRequest:request]] waitUntilFinished:YES];
279 if ([request error]) {
280 alert = [[[NSAlert alloc] init] autorelease];
281 [alert setMessageText:@"HTTP Request Error"];
282 [alert setInformativeText:[NSString stringWithFormat:@"An error occured: %@", [request error]]];
283 [alert addButtonWithTitle:@"OK"];
286 } else if (request.responseStatusCode == 200) {
287 alert = [[[NSAlert alloc] init] autorelease];
288 [alert setMessageText:@"Apply changes"];
289 [alert setInformativeText:[NSString stringWithFormat:@"In order to apply the changes in '%@', the object at the same path must be replaced. Continue?", self.displayName]];
290 [alert addButtonWithTitle:@"OK"];
291 [alert addButtonWithTitle:@"Cancel"];
292 NSInteger choice = [alert runModal];
293 if (choice == NSAlertFirstButtonReturn) {
294 request = [ASIPithosObjectRequest deleteObjectRequestWithPithos:pithos
295 containerName:pithosContainer.name
297 ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
299 [networkQueue addOperations:[NSArray arrayWithObject:[PithosUtilities prepareRequest:request]] waitUntilFinished:YES];
300 if ([request error]) {
301 alert = [[[NSAlert alloc] init] autorelease];
302 [alert setMessageText:@"HTTP Request Error"];
303 [alert setInformativeText:[NSString stringWithFormat:@"An error occured: %@", [request error]]];
304 [alert addButtonWithTitle:@"OK"];
307 } else if (request.responseStatusCode != 204) {
308 [PithosUtilities unexpectedResponseStatusAlertWithRequest:request];
316 } else if (request.responseStatusCode == 404) {
319 [PithosUtilities unexpectedResponseStatusAlertWithRequest:request];
323 [[pithosNodeInfoController window] makeFirstResponder:nil];
324 applyMetadataObjectRequest = [[ASIPithosObjectRequest writeObjectDataRequestWithPithos:pithos
325 containerName:pithosContainer.name
328 contentType:@"application/directory"
329 contentEncoding:pithosObject.contentEncoding
330 contentDisposition:pithosObject.contentDisposition
331 manifest:pithosObject.manifest
332 sharing:pithosObject.sharing
333 isPublic:(isPublic ? ASIPithosObjectRequestPublicTrue : ASIPithosObjectRequestPublicFalse)
334 metadata:pithosObject.metadata
335 data:[NSData data]] retain];
336 pithosObject.subdir = NO;
337 applyMetadataObjectRequest.delegate = self;
338 applyMetadataObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
339 applyMetadataObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
340 applyMetadataObjectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
341 [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority",
342 [NSNumber numberWithUnsignedInteger:10], @"retries",
343 NSStringFromSelector(@selector(objectRequestFinished:)), @"didFinishSelector",
344 NSStringFromSelector(@selector(objectRequestFailed:)), @"didFailSelector",
346 [[PithosUtilities prepareRequest:applyMetadataObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
349 [[pithosNodeInfoController window] makeFirstResponder:nil];
350 if (sharingAccount) {
351 applyMetadataObjectRequest = [[ASIPithosObjectRequest updateObjectMetadataRequestWithPithos:pithos
352 containerName:pithosContainer.name
353 objectName:pithosObject.name
355 contentDisposition:nil
358 isPublic:(isPublic ? ASIPithosObjectRequestPublicTrue : ASIPithosObjectRequestPublicFalse)
359 metadata:pithosObject.metadata
361 [applyMetadataObjectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
363 applyMetadataObjectRequest = [[ASIPithosObjectRequest updateObjectMetadataRequestWithPithos:pithos
364 containerName:pithosContainer.name
365 objectName:pithosObject.name
366 contentEncoding:pithosObject.contentEncoding
367 contentDisposition:pithosObject.contentDisposition
368 manifest:pithosObject.manifest
369 sharing:(pithosObject.sharing ? pithosObject.sharing : @"")
370 isPublic:(isPublic ? ASIPithosObjectRequestPublicTrue : ASIPithosObjectRequestPublicFalse)
371 metadata:pithosObject.metadata
374 applyMetadataObjectRequest.delegate = self;
375 applyMetadataObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
376 applyMetadataObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
377 applyMetadataObjectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
378 [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority",
379 [NSNumber numberWithUnsignedInteger:10], @"retries",
380 NSStringFromSelector(@selector(objectRequestFinished:)), @"didFinishSelector",
381 NSStringFromSelector(@selector(objectRequestFailed:)), @"didFailSelector",
383 [[PithosUtilities prepareRequest:applyMetadataObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
389 - (void)refreshInfo {
390 @synchronized(self) {
391 if (pithosObject.subdir) {
392 self.pithosObject = [ASIPithosObject subdirWithName:pithosObject.name];
394 } else if (refreshMetadataObjectRequest == nil) {
395 refreshMetadataObjectRequest = [[ASIPithosObjectRequest objectMetadataRequestWithPithos:pithos
396 containerName:pithosContainer.name
397 objectName:prefix] retain];
399 [refreshMetadataObjectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
400 refreshMetadataObjectRequest.delegate = self;
401 refreshMetadataObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
402 refreshMetadataObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
403 refreshMetadataObjectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
404 [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority",
405 [NSNumber numberWithUnsignedInteger:10], @"retries",
406 NSStringFromSelector(@selector(objectRequestFinished:)), @"didFinishSelector",
407 NSStringFromSelector(@selector(objectRequestFailed:)), @"didFailSelector",
409 refreshMetadataObjectRequest.downloadCache = [ASIDownloadCache sharedCache];
410 [[PithosUtilities prepareRequest:refreshMetadataObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
413 [self refreshVersions];
417 #pragma mark Versions
419 - (void)refreshVersions {
420 @synchronized(self) {
421 if (pithosObject.subdir) {
423 } else if (refreshVersionsObjectRequest == nil) {
424 refreshVersionsObjectRequest = [[ASIPithosObjectRequest objectVersionsRequestWithPithos:pithos
425 containerName:pithosContainer.name
426 objectName:pithosObject.name] retain];
428 [refreshVersionsObjectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
429 refreshVersionsObjectRequest.delegate = self;
430 refreshVersionsObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
431 refreshVersionsObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
432 refreshVersionsObjectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
433 [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority",
434 [NSNumber numberWithUnsignedInteger:10], @"retries",
435 NSStringFromSelector(@selector(objectRequestFinished:)), @"didFinishSelector",
436 NSStringFromSelector(@selector(objectRequestFailed:)), @"didFailSelector",
438 refreshVersionsObjectRequest.downloadCache = [ASIDownloadCache sharedCache];
439 [[PithosUtilities prepareRequest:refreshVersionsObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
447 - (void)showPithosNodeInfo:(id)sender {
448 if (!pithosNodeInfoController) {
449 pithosNodeInfoController = [[PithosObjectNodeInfoController alloc] initWithPithosNode:self];
452 [pithosNodeInfoController showWindow:sender];
453 [[pithosNodeInfoController window] makeKeyAndOrderFront:sender];
454 [NSApp activateIgnoringOtherApps:YES];