d3a08900f9b6b71cc9e5fa0e876b47ee107114fa
[pithos-macos] / pithos-macos / PithosContainerNode.m
1 //
2 //  PithosContainerNode.m
3 //  pithos-macos
4 //
5 // Copyright 2011-2012 GRNET S.A. All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or
8 // without modification, are permitted provided that the following
9 // conditions are met:
10 // 
11 //   1. Redistributions of source code must retain the above
12 //      copyright notice, this list of conditions and the following
13 //      disclaimer.
14 // 
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.
19 // 
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.
32 // 
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.
37
38 #import "PithosContainerNode.h"
39 #import "PithosObjectNode.h"
40 #import "PithosSubdirNode.h"
41 #import "ASIPithos.h"
42 #import "ASIPithosContainerRequest.h"
43 #import "ASIPithosContainer.h"
44 #import "ASIPithosObject.h"
45 #import "ASIDownloadCache.h"
46 #import "PithosUtilities.h"
47 #import "PithosContainerNodeInfoController.h"
48 #import "PithosActivityFacility.h"
49
50 static NSImage *sharedIcon = nil;
51
52 @implementation PithosContainerNode
53 @synthesize pithos, pithosContainer, prefix;
54 @synthesize policyVersioning, policyQuota;
55
56 + (void)initialize {
57         if (self == [PithosContainerNode class])
58         sharedIcon = [[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericHardDiskIcon)] retain];
59 }
60
61 #pragma mark -
62 #pragma mark Object Lifecycle
63
64 - (id)initWithPithos:(ASIPithos *)aPithos pithosContainer:(ASIPithosContainer *)aPithosContainer icon:(NSImage *)anIcon {
65     if ((self = [super init])) {
66         self.pithos = aPithos;
67         self.pithosContainer = aPithosContainer;
68         prefix = nil;
69         self.icon = anIcon;
70     }
71     return self;
72 }
73
74 - (id)initWithPithos:(ASIPithos *)aPithos pithosContainer:(ASIPithosContainer *)aPithosContainer {
75     return [self initWithPithos:aPithos pithosContainer:aPithosContainer icon:nil];
76 }
77
78 - (id)initWithPithos:(ASIPithos *)aPithos containerName:(NSString *)aContainerName icon:(NSImage *)anIcon {
79     ASIPithosContainer *container = [ASIPithosContainer container];
80     container.name = aContainerName;
81     return [self initWithPithos:aPithos pithosContainer:container icon:anIcon];
82 }
83
84 - (id)initWithPithos:(ASIPithos *)aPithos containerName:(NSString *)aContainerName {
85     return [self initWithPithos:aPithos containerName:aContainerName icon:nil];
86 }
87
88 - (void)dealloc {
89     [containerRequest clearDelegatesAndCancel];
90     [containerRequest release];
91     [refreshMetadataContainerRequest clearDelegatesAndCancel];
92     [refreshMetadataContainerRequest release];
93     [applyMetadataContainerRequest clearDelegatesAndCancel];
94     [applyMetadataContainerRequest release];
95     [policyQuota release];
96     [policyVersioning release];
97     [prefix release];
98     [objects release];
99     [pithosContainer release];
100     [pithos release];
101     [super dealloc];
102 }
103
104 #pragma mark -
105 #pragma mark Properties
106
107 - (void)setPithos:(ASIPithos *)aPithos {
108     if (aPithos && ![aPithos isEqualTo:pithos]) {
109         [pithos release];
110         pithos = [aPithos retain];
111         [url release];
112         url = nil;
113     }
114 }
115
116 - (NSString *)url {
117     if (url == nil)
118         url = [[NSString alloc] initWithFormat:@"%@/%@%@", 
119                (sharingAccount ? [pithos storageURLWithAuthUser:sharingAccount] : pithos.storageURL), 
120                pithosContainer.name, 
121                (shared ? @"?shared" : @"")];
122     return url;
123 }
124
125 - (NSArray *)children {
126     @synchronized(self) {
127         switch (freshness) {
128             case PithosNodeStateFresh:
129                 break;
130             case PithosNodeStateRefreshNeeded:
131                 freshness = PithosNodeStateRefreshing;
132                 containerRequest = [[ASIPithosContainerRequest listObjectsRequestWithPithos:pithos 
133                                                                               containerName:pithosContainer.name 
134                                                                                       limit:0 
135                                                                                      marker:nil 
136                                                                                      prefix:prefix 
137                                                                                   delimiter:@"/" 
138                                                                                        path:nil 
139                                                                                        meta:nil 
140                                                                                      shared:shared 
141                                                                                       until:nil] retain];
142                 if (sharingAccount)
143                     [containerRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
144                 else if (!forcedRefresh)
145                     containerRequest.downloadCache = [ASIDownloadCache sharedCache];
146                 containerRequest.delegate = self;
147                 containerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
148                 containerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
149                 containerRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
150                                              [NSNumber numberWithInteger:NSOperationQueuePriorityVeryHigh], @"priority", 
151                                              [NSNumber numberWithUnsignedInteger:10], @"retries", 
152                                              NSStringFromSelector(@selector(containerRequestFinished:)), @"didFinishSelector", 
153                                              NSStringFromSelector(@selector(containerRequestFailed:)), @"didFailSelector", 
154                                              nil];
155                 [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
156                 break;
157             case PithosNodeStateRefreshing:
158                 break;
159             case PithosNodeStateRefreshFinished:
160                 if (newChildren) {
161                     [children release];
162                     children = newChildren;
163                     newChildren = nil;
164                 }
165                 freshness = PithosNodeStateFresh;
166             default:
167                 break;
168         }
169         return children;
170     }
171 }
172
173 - (NSString *)displayName {
174     return [[pithosContainer.name copy] autorelease];
175 }
176
177 - (void)setDisplayName:(NSString *)aDisplayName {
178 }
179
180 - (NSImage *)icon {
181     if (icon == nil) {
182         if ([pithosContainer.name isEqualToString:@"pithos"])
183             icon = [[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kToolbarHomeIcon)] retain];
184         else if ([pithosContainer.name isEqualToString:@"trash"])
185             icon = [[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kFullTrashIcon)] retain];
186         else
187             icon = [sharedIcon retain];
188     }
189     return icon;
190 }
191
192 - (void)setPithosContainer:(ASIPithosContainer *)aPithosContainer {
193     if (![pithosContainer isEqualTo:aPithosContainer]) {
194         [pithosContainer release];
195         pithosContainer = [aPithosContainer retain];
196     }
197     if (pithosContainer.policy) {
198         self.policyVersioning = [pithosContainer.policy objectForKey:@"versioning"];
199         self.policyQuota = [NSNumber numberWithLongLong:[[pithosContainer.policy objectForKey:@"quota"] longLongValue]];
200     } else {
201         self.policyVersioning = @"manual";
202         self.policyQuota = [NSNumber numberWithLongLong:0];
203     }
204 }
205
206 - (void)setLimitedPithosContainer:(ASIPithosContainer *)aPithosContainer {
207     if (![pithosContainer isEqualTo:aPithosContainer]) {
208         self.pithosContainer.name = aPithosContainer.name;
209         self.pithosContainer.count = aPithosContainer.count;
210         self.pithosContainer.bytes = aPithosContainer.bytes;
211         self.pithosContainer.lastModified = aPithosContainer.lastModified;
212         self.pithosContainer.untilTimestamp = aPithosContainer.untilTimestamp;
213         if (!pithosNodeInfoController) {
214             self.pithosContainer.policy = aPithosContainer.policy;
215             self.pithosContainer = pithosContainer;
216         }
217     }
218 }
219
220 #pragma mark -
221 #pragma mark ASIHTTPRequestDelegate
222
223 - (void)containerRequestFailed:(ASIPithosContainerRequest *)request {
224     @autoreleasepool {
225         NSString *message;
226         NSError *error = [containerRequest error];
227         if (error)
228             message = [NSString stringWithFormat:@"Container listing %@ failed: %@", containerRequest.url, [error localizedDescription]];
229         else
230             message = [NSString stringWithFormat:@"Container listing %@ failed: (%d) %@", 
231                        containerRequest.url, containerRequest.responseStatusCode, containerRequest.responseStatusMessage];
232         dispatch_async(dispatch_get_main_queue(), ^{
233             [[PithosActivityFacility defaultPithosActivityFacility] startAndEndActivityWithType:PithosActivityOther message:message];
234         });
235         NSUInteger retries = [[containerRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
236         if (retries > 0) {
237             ASIPithosContainerRequest *newContainerRequest = (ASIPithosContainerRequest *)[PithosUtilities copyRequest:containerRequest];
238             [(NSMutableDictionary *)(newContainerRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
239             [containerRequest release];
240             containerRequest = newContainerRequest;
241             [[PithosUtilities prepareRequest:containerRequest priority:[[containerRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous];
242         } else {
243             [newChildren release];
244             newChildren = nil;
245             [containerRequest release];
246             containerRequest = nil;
247             [objects release];
248             objects = nil;
249             forcedRefresh = NO;
250             @synchronized(self) {
251                 freshness = PithosNodeStateRefreshNeeded;
252             }
253         }
254     }
255 }
256
257 - (void)containerRequestFinished:(ASIPithosContainerRequest *)request {
258     @autoreleasepool {
259         DLog(@"List container finished: %@", [containerRequest url]);
260         DLog(@"Cached: %d", [containerRequest didUseCachedResponse]);
261         if (containerRequest.responseStatusCode == 200) {
262             if ((pithosContainer.blockHash == nil) || (pithosContainer.blockSize == 0)) {
263                 pithosContainer.blockHash = [containerRequest blockHash];
264                 pithosContainer.blockSize = [containerRequest blockSize];
265             }
266         
267             NSArray *someObjects = [containerRequest objects];
268             if (objects == nil) {
269                 objects = [[NSMutableArray alloc] initWithArray:someObjects];
270             } else {
271                 [objects addObjectsFromArray:someObjects];
272             }
273             if ([someObjects count] < 10000) {
274                 if (!containerRequest.didUseCachedResponse || ([objects count] != [someObjects count]) || !children) {
275                     // Save new children
276                     DLog(@"using newChildren");
277                     newChildren = [[NSMutableArray alloc] init];
278                     NSArray *objectNames = [objects valueForKey:@"name"];
279                     NSMutableIndexSet *keptNodes = [NSMutableIndexSet indexSet];
280                     BOOL isSubdirNode = ([self class] == [PithosSubdirNode class]);
281                     for (ASIPithosObject *object in objects) {
282                         if (!isSubdirNode || 
283                             ([object.name hasPrefix:[((PithosSubdirNode *)self).prefix stringByAppendingString:@"/"]] &&
284                              ([object.name length] > [((PithosSubdirNode *)self).prefix length] + 1))) {
285                             // The check above removes false objects due to trailing slash or same prefix
286                             if (object.subdir) {
287                                 NSUInteger sameNameObjectIndex = [objectNames indexOfObject:[object.name substringToIndex:([object.name length] - 1)]];
288                                 if ((sameNameObjectIndex == NSNotFound) || 
289                                     ![PithosUtilities isContentTypeDirectory:[[objects objectAtIndex:sameNameObjectIndex] contentType]]) {
290                                     PithosSubdirNode *node = [[[PithosSubdirNode alloc] initWithPithos:pithos 
291                                                                                        pithosContainer:pithosContainer 
292                                                                                           pithosObject:object] autorelease];
293                                     node.parent = self;
294                                     node.shared = shared;
295                                     node.sharingAccount = sharingAccount;
296                                     node.inheritChildrenUpdatedNotificationName = inheritChildrenUpdatedNotificationName;
297                                     if (children) {
298                                         NSUInteger oldIndex = [children indexOfObject:node];
299                                         if (oldIndex != NSNotFound) {
300                                             // Use the same pointer value, if possible
301                                             node = [children objectAtIndex:oldIndex];
302                                             node.pithosContainer = pithosContainer;
303 //                                          node.pithosObject = object;
304                                             [node setLimitedPithosObject:object];
305                                             [keptNodes addIndex:oldIndex];
306                                         }
307                                     }
308                                     if (sharingAccount)
309                                         node.pithosObject.allowedTo = @"read";
310                                     [newChildren addObject:node];
311                                 }
312                             } else if ([PithosUtilities isContentTypeDirectory:object.contentType]) {
313                                 PithosSubdirNode *node = [[[PithosSubdirNode alloc] initWithPithos:pithos 
314                                                                                    pithosContainer:pithosContainer 
315                                                                                       pithosObject:object] autorelease];
316                                 node.parent = self;
317                                 node.shared = shared;
318                                 node.sharingAccount = sharingAccount;
319                                 node.inheritChildrenUpdatedNotificationName = inheritChildrenUpdatedNotificationName;
320                                 if (children) {
321                                     NSUInteger oldIndex = [children indexOfObject:node];
322                                     if (oldIndex != NSNotFound) {
323                                         // Use the same pointer value, if possible
324                                         node = [children objectAtIndex:oldIndex];
325                                         node.pithosContainer = pithosContainer;
326 //                                      node.pithosObject = object;
327                                         [node setLimitedPithosObject:object];
328                                         [keptNodes addIndex:oldIndex];
329                                     }
330                                 }
331                                 [newChildren addObject:node];
332                             } else {
333                                 PithosObjectNode *node = [[[PithosObjectNode alloc] initWithPithos:pithos 
334                                                                                    pithosContainer:pithosContainer 
335                                                                                       pithosObject:object] autorelease];
336                                 node.parent = self;
337                                 node.shared = shared;
338                                 node.sharingAccount = sharingAccount;
339                                 node.inheritChildrenUpdatedNotificationName = inheritChildrenUpdatedNotificationName;
340                                 if (children) {
341                                     NSUInteger oldIndex = [children indexOfObject:node];
342                                     if (oldIndex != NSNotFound) {
343                                         // Use the same pointer value, if possible
344                                         node = [children objectAtIndex:oldIndex];
345                                         node.pithosContainer = pithosContainer;
346 //                                      node.pithosObject = object;
347                                         [node setLimitedPithosObject:object];
348                                         [keptNodes addIndex:oldIndex];
349                                     }
350                                 }
351                                 [newChildren addObject:node];                                
352                             }
353                         }
354                     }
355                     [[children objectsAtIndexes:
356                       [[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [children count])] indexesPassingTest:^(NSUInteger idx, BOOL *stop){
357                         if ([keptNodes containsIndex:idx])
358                             return NO;
359                         return YES;
360                     }]] makeObjectsPerformSelector:@selector(pithosNodeWillBeRemoved)];
361                 }
362                 // Else cache was used and all results were fetched during this request, so existing children can be reused
363                 [containerRequest release];
364                 containerRequest = nil;
365                 [objects release];
366                 objects = nil;
367                 forcedRefresh = NO;
368                 @synchronized(self) {
369                     freshness = PithosNodeStateRefreshFinished;
370                 }
371                 [self postChildrenUpdatedNotificationName];
372             } else {
373                 [containerRequest release];
374                 // Do an additional request to fetch more objects
375                 containerRequest = [[ASIPithosContainerRequest listObjectsRequestWithPithos:pithos 
376                                                                               containerName:pithosContainer.name 
377                                                                                       limit:0 
378                                                                                      marker:[[someObjects lastObject] name] 
379                                                                                      prefix:prefix 
380                                                                                   delimiter:@"/" 
381                                                                                        path:nil 
382                                                                                        meta:nil 
383                                                                                      shared:shared 
384                                                                                       until:nil] retain];
385                 if (sharingAccount)
386                     [containerRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
387                 else if (!forcedRefresh)
388                     containerRequest.downloadCache = [ASIDownloadCache sharedCache];
389                 containerRequest.delegate = self;
390                 containerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
391                 containerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
392                 containerRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
393                                              [NSNumber numberWithInteger:NSOperationQueuePriorityVeryHigh], @"priority", 
394                                              [NSNumber numberWithUnsignedInteger:10], @"retries", 
395                                              NSStringFromSelector(@selector(containerRequestFinished:)), @"didFinishSelector", 
396                                              NSStringFromSelector(@selector(containerRequestFailed:)), @"didFailSelector", 
397                                              nil];
398                 [[PithosUtilities prepareRequest:containerRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
399             }
400         } else if (containerRequest.responseStatusCode == 304) {
401             // Container is not modified, so existing children can be reused
402             [containerRequest release];
403             containerRequest = nil;
404             [objects release];
405             objects = nil;
406             forcedRefresh = NO;
407             @synchronized(self) {
408                 freshness = PithosNodeStateRefreshFinished;
409             }
410             [self postChildrenUpdatedNotificationName];
411         } else {
412             [self containerRequestFailed:containerRequest];
413         }
414     }
415 }
416
417 - (void)containerMetadataRequestFinished:(ASIPithosContainerRequest *)request {
418     @autoreleasepool {
419         DLog(@"URL: %@", [request url]);
420         DLog(@"cached: %d", [request didUseCachedResponse]);
421         
422         if ([request isEqualTo:applyMetadataContainerRequest]) {
423             @synchronized(self) {
424                 [applyMetadataContainerRequest release];
425                 applyMetadataContainerRequest = nil;
426             }
427             [self refreshInfo];
428         } else if ([request isEqualTo:refreshMetadataContainerRequest]) {
429             [[pithosNodeInfoController window] makeFirstResponder:nil];
430             self.pithosContainer = [refreshMetadataContainerRequest container];
431             @synchronized(self) {
432                 [refreshMetadataContainerRequest release];
433                 refreshMetadataContainerRequest = nil;
434             }
435         }
436     }
437 }
438
439 - (void)containerMetadataRequestFailed:(ASIPithosContainerRequest *)request {
440     @autoreleasepool {
441         NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue];
442         if (retries > 0) {
443             ASIPithosContainerRequest *newRequest = (ASIPithosContainerRequest *)[PithosUtilities copyRequest:request];
444             [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
445             if ([request isEqualTo:applyMetadataContainerRequest]) {
446                 @synchronized(self) {
447                     [applyMetadataContainerRequest release];
448                     applyMetadataContainerRequest = newRequest;
449                 }
450             } else if ([request isEqualTo:refreshMetadataContainerRequest]) {
451                 @synchronized(self) {
452                     [refreshMetadataContainerRequest release];
453                     refreshMetadataContainerRequest = newRequest;
454                 }
455             }
456             [[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous];
457         } else {
458             if ([request isEqualTo:applyMetadataContainerRequest]) {
459                 [PithosUtilities httpRequestErrorAlertWithRequest:applyMetadataContainerRequest];
460                 @synchronized(self) {
461                     [applyMetadataContainerRequest release];
462                     applyMetadataContainerRequest = nil;
463                 }
464             } else if ([request isEqualTo:refreshMetadataContainerRequest]) {
465                 [PithosUtilities httpRequestErrorAlertWithRequest:refreshMetadataContainerRequest];
466                 @synchronized(self) {
467                     [refreshMetadataContainerRequest release];
468                     refreshMetadataContainerRequest = nil;
469                 }
470             }
471         }
472     }
473 }
474
475 #pragma mark -
476 #pragma mark Info
477
478 - (void)applyInfo {
479     @synchronized(self) {
480         if (applyMetadataContainerRequest == nil) {
481             [[pithosNodeInfoController window] makeFirstResponder:nil];
482             applyMetadataContainerRequest = [[ASIPithosContainerRequest updateContainerMetadataRequestWithPithos:pithos 
483                                                                                                    containerName:pithosContainer.name 
484                                                                                                           policy:[NSDictionary dictionaryWithObjectsAndKeys:
485                                                                                                                   policyVersioning, @"versioning", 
486                                                                                                                   [policyQuota stringValue], @"quota", 
487                                                                                                                   nil] 
488                                                                                                         metadata:pithosContainer.metadata 
489                                                                                                           update:NO] retain];
490             applyMetadataContainerRequest.delegate = self;
491             applyMetadataContainerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
492             applyMetadataContainerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
493             applyMetadataContainerRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
494                                                       [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
495                                                       [NSNumber numberWithUnsignedInteger:10], @"retries", 
496                                                       NSStringFromSelector(@selector(containerMetadataRequestFinished:)), @"didFinishSelector", 
497                                                       NSStringFromSelector(@selector(containerMetadataRequestFailed:)), @"didFailSelector", 
498                                                       nil];
499             [[PithosUtilities prepareRequest:applyMetadataContainerRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
500         }
501     }
502 }
503
504 - (void)refreshInfo {
505     @synchronized(self) {
506         if (refreshMetadataContainerRequest == nil) {
507             refreshMetadataContainerRequest = [[ASIPithosContainerRequest containerMetadataRequestWithPithos:pithos 
508                                                                                                containerName:pithosContainer.name] retain];
509             refreshMetadataContainerRequest.delegate = self;
510             refreshMetadataContainerRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
511             refreshMetadataContainerRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
512             refreshMetadataContainerRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
513                                                         [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
514                                                         [NSNumber numberWithUnsignedInteger:10], @"retries", 
515                                                         NSStringFromSelector(@selector(containerMetadataRequestFinished:)), @"didFinishSelector", 
516                                                         NSStringFromSelector(@selector(containerMetadataRequestFailed:)), @"didFailSelector", 
517                                                         nil];
518             if (!sharingAccount)
519                 refreshMetadataContainerRequest.downloadCache = [ASIDownloadCache sharedCache];
520             [[PithosUtilities prepareRequest:refreshMetadataContainerRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
521         }
522     }
523 }
524
525 #pragma mark -
526 #pragma mark Actions
527
528 - (void)showPithosNodeInfo:(id)sender {
529     if (!pithosNodeInfoController) {
530         pithosNodeInfoController = [[PithosContainerNodeInfoController alloc] initWithPithosNode:self];
531         [self refreshInfo];
532     }
533     [pithosNodeInfoController showWindow:sender];
534     [[pithosNodeInfoController window] makeKeyAndOrderFront:sender];
535     [NSApp activateIgnoringOtherApps:YES];
536 }
537
538 @end