69029b55b28370f1e330510eda1936a5328c20ea
[pithos-macos] / pithos-macos / PithosAccountNode.m
1 //
2 //  PithosAccountNode.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 "PithosAccountNode.h"
39 #import "PithosContainerNode.h"
40 #import "ASIPithos.h"
41 #import "ASIPithosAccountRequest.h"
42 #import "ASIPithosAccount.h"
43 #import "ASIPithosContainer.h"
44 #import "ASIDownloadCache.h"
45 #import "PithosAccount.h"
46 #import "PithosUtilities.h"
47 #import "PithosActivityFacility.h"
48
49 static NSImage *sharedIcon = nil;
50
51 @implementation PithosAccountNode
52 @synthesize pithos, pithosAccount, accountRequest, applyMetadataAccountRequest, refreshMetadataAccountRequest, translatedGroups;
53
54 + (void)initialize {
55         if (self == [PithosAccountNode class])
56         sharedIcon = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kUserIcon)];
57 }
58
59 #pragma mark -
60 #pragma mark Object Lifecycle
61
62 - (id)initWithPithosAccountManager:(PithosAccount *)aPithosAccountManager andPithos:(ASIPithos *)aPithos {
63     if ((self = [super initWithPithosAccountManager:aPithosAccountManager])) {
64         pithos = aPithos;
65     }
66     return self;
67 }
68
69 - (void)dealloc {
70     [accountRequest clearDelegatesAndCancel];
71     [refreshMetadataAccountRequest clearDelegatesAndCancel];
72     [applyMetadataAccountRequest clearDelegatesAndCancel];
73 }
74
75 #pragma mark -
76 #pragma mark Internal
77
78 - (void)updateGroups {
79     if (!pithosAccount) {
80         self.translatedGroups = [NSMutableDictionary dictionary];
81     } else if (pithosAccountManager) {
82         NSMutableSet *UUIDs = [NSMutableSet set];
83         for (NSString *groupName in pithosAccount.groups) {
84             [UUIDs addObjectsFromArray:[pithosAccount.groups objectForKey:groupName]];
85         }
86         [UUIDs removeObject:@""];
87         [UUIDs removeObject:@"*"];
88         if (UUIDs.count) {
89             [pithosAccountManager updateUserCatalogForDisplaynames:nil UUIDs:[UUIDs allObjects]];
90         }
91         
92         NSMutableDictionary *newTranslatedGroups = [NSMutableDictionary dictionaryWithCapacity:pithosAccount.groups.count];
93         for (NSString *groupName in pithosAccount.groups) {
94             NSMutableArray *groupUsers = [NSMutableArray array];
95             for (NSString *UUID in [pithosAccount.groups objectForKey:groupName]) {
96                 [groupUsers addObject:[pithosAccountManager displaynameForUUID:UUID safe:YES]];
97             }
98             [newTranslatedGroups setObject:groupUsers forKey:groupName];
99         }
100         self.translatedGroups = newTranslatedGroups;
101     } else {
102         self.translatedGroups = [pithosAccount.groups copy];
103     }
104 }
105
106 #pragma mark -
107 #pragma mark Properties
108
109 - (void)setPithos:(ASIPithos *)aPithos {
110     if (aPithos && ![aPithos isEqualTo:pithos]) {
111         pithos = aPithos;
112         url = nil;
113         [accountRequest clearDelegatesAndCancel];
114         self.accountRequest = nil;
115         [refreshMetadataAccountRequest clearDelegatesAndCancel];
116         self.refreshMetadataAccountRequest = nil;
117         [applyMetadataAccountRequest clearDelegatesAndCancel];
118         self.applyMetadataAccountRequest = nil;
119         reset = YES;
120     }
121 }
122
123 - (void)setPithosAccount:(ASIPithosAccount *)aPithosAccount {
124     if (![pithosAccount isEqualTo:aPithosAccount]) {
125         pithosAccount = aPithosAccount;
126         [self updateGroups];
127     }
128 }
129
130 - (NSString *)url {
131     if (url == nil)
132         url = [[NSString alloc] initWithFormat:@"%@%@", 
133                (sharingAccount ? [pithos storageURLWithAuthUser:sharingAccount] : pithos.storageURL),
134                (shared ? @"?shared" : @"")];
135     return url;
136 }
137
138 - (NSArray *)children {
139     @synchronized(self) {
140         if (reset) {
141             [accountRequest clearDelegatesAndCancel];
142             self.accountRequest = nil;
143             [refreshMetadataAccountRequest clearDelegatesAndCancel];
144             self.refreshMetadataAccountRequest = nil;
145             [applyMetadataAccountRequest clearDelegatesAndCancel];
146             self.applyMetadataAccountRequest = nil;
147             children = nil;
148             newChildren = nil;
149             self.pithosAccount = nil;
150             freshness = PithosNodeStateRefreshNeeded;
151             forcedRefresh = YES;
152             reset = NO;
153             [self postChildrenUpdatedNotificationName];
154         }
155         switch (freshness) {
156             case PithosNodeStateFresh:
157                 break;
158             case PithosNodeStateRefreshNeeded:
159                 freshness = PithosNodeStateRefreshing;
160                 self.accountRequest = [ASIPithosAccountRequest listContainersRequestWithPithos:pithos
161                                                                                          limit:0
162                                                                                         marker:nil
163                                                                                         shared:shared
164                                                                                          until:nil];
165                 if (sharingAccount)
166                     [accountRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
167                 else if (!forcedRefresh)
168                     accountRequest.downloadCache = [ASIDownloadCache sharedCache];
169                 accountRequest.delegate = self;
170                 accountRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
171                 accountRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
172                 accountRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
173                                            [NSNumber numberWithInteger:NSOperationQueuePriorityVeryHigh], @"priority", 
174                                            [NSNumber numberWithUnsignedInteger:10], @"retries", 
175                                            NSStringFromSelector(@selector(accountRequestFinished:)), @"didFinishSelector", 
176                                            NSStringFromSelector(@selector(accountRequestFailed:)), @"didFailSelector", 
177                                            nil];
178                 [[PithosUtilities prepareRequest:accountRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
179                 break;
180             case PithosNodeStateRefreshing:
181                 break;
182             case PithosNodeStateRefreshFinished:
183                 if (newChildren) {
184                     children = newChildren;
185                     newChildren = nil;
186                 }
187                 freshness = PithosNodeStateFresh;
188             default:
189                 break;
190         }
191         return children;
192     }
193 }
194
195 - (NSString *)displayName {
196     if (displayName == nil) {
197         if (!sharingAccount) {
198             return @"account";
199         } else if (pithosAccountManager) {
200             return [pithosAccountManager displaynameForUUID:sharingAccount safe:YES];
201         } else {
202             return [sharingAccount copy];
203         }
204     }
205     return [displayName copy];
206 }
207
208 - (NSImage *)icon {
209     if (icon == nil)
210         icon = sharedIcon;
211     return icon;
212 }
213
214 #pragma mark -
215 #pragma mark ASIHTTPRequestDelegate
216
217 - (void)accountRequestFailed:(ASIPithosAccountRequest *)request {
218     @autoreleasepool {
219         NSString *message;
220         NSError *error = [accountRequest error];
221         if (error)
222             message = [NSString stringWithFormat:@"Account listing %@ failed: %@", accountRequest.url, [error localizedDescription]];
223         else
224             message = [NSString stringWithFormat:@"Account listing %@ failed: (%d) %@", 
225                        accountRequest.url, accountRequest.responseStatusCode, accountRequest.responseStatusMessage];
226         dispatch_async(dispatch_get_main_queue(), ^{
227             [[PithosActivityFacility defaultPithosActivityFacility] startAndEndActivityWithType:PithosActivityOther message:message];
228         });
229         NSUInteger retries = [[accountRequest.userInfo objectForKey:@"retries"] unsignedIntegerValue];
230         if (retries > 0) {
231             ASIPithosAccountRequest *newAccountRequest = (ASIPithosAccountRequest *)[PithosUtilities copyRequest:accountRequest];
232             [(NSMutableDictionary *)(newAccountRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
233             self.accountRequest = newAccountRequest;
234             [[PithosUtilities prepareRequest:accountRequest priority:[[accountRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous];
235         } else {
236             newChildren = nil;
237             self.accountRequest = nil;
238             forcedRefresh = NO;
239             @synchronized(self) {
240                 freshness = PithosNodeStateRefreshNeeded;
241             }
242         }
243     }
244 }
245
246 - (void)accountRequestFinished:(ASIPithosAccountRequest *)request {
247     @autoreleasepool {
248         DLog(@"List account finished: %@", [accountRequest url]);
249         DLog(@"Cached: %d", [accountRequest didUseCachedResponse]);
250         if (accountRequest.responseStatusCode == 200) {
251             self.pithosAccount = [accountRequest account];
252             
253             NSMutableArray *containers = [accountRequest.userInfo objectForKey:@"containers"];
254             NSArray *someContainers = [accountRequest containers];
255             if (containers == nil) {
256                 containers = [NSMutableArray arrayWithArray:someContainers];
257             } else {
258                 [containers addObjectsFromArray:someContainers];
259             }
260             if ([someContainers count] < 10000) {
261                 if (!accountRequest.didUseCachedResponse || ([containers count] != [someContainers count]) || !children) {
262                     // Save new children
263                     DLog(@"using newChildren");
264                     newChildren = [[NSMutableArray alloc] init];
265                     NSMutableIndexSet *keptNodes = [NSMutableIndexSet indexSet];
266                     for (ASIPithosContainer *container in containers) {
267                         PithosContainerNode *node = [[PithosContainerNode alloc] initWithPithosAccountManager:pithosAccountManager
268                                                                                                     andPithos:pithos
269                                                                                               pithosContainer:container];
270                         node.parent = self;
271                         node.shared = shared;
272                         node.sharingAccount = sharingAccount;
273                         node.inheritChildrenUpdatedNotificationName = inheritChildrenUpdatedNotificationName;
274                         if (children) {
275                             NSUInteger oldIndex = [children indexOfObject:node];
276                             if (oldIndex != NSNotFound) {
277                                 // Use the same pointer value, if possible
278                                 node = [children objectAtIndex:oldIndex];
279     //                            node.pithosContainer = container;
280                                 [node setLimitedPithosContainer:container];
281                                 [keptNodes addIndex:oldIndex];
282                             }
283                         }
284                         [newChildren addObject:node];
285                     }
286                     [[children objectsAtIndexes:
287                       [[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [children count])] indexesPassingTest:^(NSUInteger idx, BOOL *stop){
288                         if ([keptNodes containsIndex:idx])
289                             return NO;
290                         return YES;
291                     }]] makeObjectsPerformSelector:@selector(pithosNodeWillBeRemoved)];
292                 }
293                 // Else cache was used and all results were fetched during this request, so existing children can be reused
294                 self.accountRequest = nil;
295                 forcedRefresh = NO;
296                 @synchronized(self) {
297                     freshness = PithosNodeStateRefreshFinished;
298                 }
299                 [self postChildrenUpdatedNotificationName];
300             } else {
301                 // Do an additional request to fetch more objects
302                 self.accountRequest = [ASIPithosAccountRequest listContainersRequestWithPithos:pithos
303                                                                                          limit:0
304                                                                                         marker:[[someContainers lastObject] name]
305                                                                                         shared:shared
306                                                                                          until:nil];
307                 if (sharingAccount)
308                     [accountRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
309                 else if (!forcedRefresh)
310                     accountRequest.downloadCache = [ASIDownloadCache sharedCache];
311                 accountRequest.delegate = self;
312                 accountRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
313                 accountRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
314                 accountRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
315                                            [NSNumber numberWithInteger:NSOperationQueuePriorityVeryHigh], @"priority", 
316                                            [NSNumber numberWithUnsignedInteger:10], @"retries", 
317                                            NSStringFromSelector(@selector(accountRequestFinished:)), @"didFinishSelector", 
318                                            NSStringFromSelector(@selector(accountRequestFailed:)), @"didFailSelector",
319                                            containers, @"containers",
320                                            nil];
321                 [[PithosUtilities prepareRequest:accountRequest priority:NSOperationQueuePriorityVeryHigh] startAsynchronous];
322             }
323         } else if (accountRequest.responseStatusCode == 304) {
324             // Account is not modified, so existing children can be reused
325             self.accountRequest = nil;
326             forcedRefresh = NO;
327             @synchronized(self) {
328                 freshness = PithosNodeStateRefreshFinished;
329             }
330             [self postChildrenUpdatedNotificationName];
331         } else {
332             [self accountRequestFailed:accountRequest];
333         }
334     }
335 }
336
337 - (void)accountMetadataRequestFinished:(ASIPithosAccountRequest *)request {
338     @autoreleasepool {
339         DLog(@"URL: %@", [request url]);
340         DLog(@"cached: %d", [request didUseCachedResponse]);
341         
342         if ([request isEqualTo:applyMetadataAccountRequest]) {
343             @synchronized(self) {
344                 self.applyMetadataAccountRequest = nil;
345             }
346             [self refreshInfo];
347         } else if ([request isEqualTo:refreshMetadataAccountRequest]) {
348             self.pithosAccount = [refreshMetadataAccountRequest account];
349             @synchronized(self) {
350                 self.refreshMetadataAccountRequest = nil;
351             }
352         }
353     }
354 }
355
356 - (void)accountMetadataRequestFailed:(ASIPithosAccountRequest *)request {
357     @autoreleasepool {
358         NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue];
359         if (retries > 0) {
360             ASIPithosAccountRequest *newRequest = (ASIPithosAccountRequest *)[PithosUtilities copyRequest:request];
361             [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
362             if ([request isEqualTo:applyMetadataAccountRequest]) {
363                 @synchronized(self) {
364                     self.applyMetadataAccountRequest = newRequest;
365                 }
366             } else if ([request isEqualTo:refreshMetadataAccountRequest]) {
367                 @synchronized(self) {
368                     self.refreshMetadataAccountRequest = newRequest;
369                 }
370             }
371             [[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous];
372         } else {
373             if ([request isEqualTo:applyMetadataAccountRequest]) {
374                 [PithosUtilities httpRequestErrorAlertWithRequest:applyMetadataAccountRequest];
375                 @synchronized(self) {
376                     self.applyMetadataAccountRequest = nil;
377                 }
378             } else if ([request isEqualTo:refreshMetadataAccountRequest]) {
379                 [PithosUtilities httpRequestErrorAlertWithRequest:refreshMetadataAccountRequest];
380                 @synchronized(self) {
381                     self.refreshMetadataAccountRequest = nil;
382                 }
383             }
384         }
385     }
386 }
387
388 #pragma mark -
389 #pragma mark Info
390
391 - (void)applyInfo {
392     @synchronized(self) {
393         if (applyMetadataAccountRequest == nil) {
394             NSMutableDictionary *groups = [NSMutableDictionary dictionary];
395             if (translatedGroups.count) {
396                 for (NSString *groupName in translatedGroups) {
397                     if (!groupName.length ||
398                         [groupName rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@" -_~,;"]].location != NSNotFound) {
399                         NSAlert *alert = [[NSAlert alloc] init];
400                         [alert setMessageText:@"Invalid Input"];
401                         [alert setInformativeText:@"Group names cannot be empty or contain ' ', '-', '_', '~', ',' or ';'."];
402                         [alert addButtonWithTitle:@"OK"];
403                         [alert runModal];
404                         return;
405                     }
406                 }
407                 if (pithosAccountManager) {
408                     NSMutableSet *allGroupUsers = [NSMutableSet set];
409                     for (NSMutableArray *groupUsers in [translatedGroups objectEnumerator]) {
410                         [allGroupUsers addObjectsFromArray:groupUsers];
411                     }
412                     [allGroupUsers removeObject:@""];
413                     [allGroupUsers removeObject:@"*"];
414                     if (allGroupUsers.count) {
415                         ASIPithosRequest *userCatalogRequest = [pithosAccountManager updateUserCatalogForDisplaynames:[allGroupUsers allObjects]
416                                                                                                                         UUIDs:nil];
417                         if (userCatalogRequest.error || ((userCatalogRequest.responseStatusCode != 200) && (userCatalogRequest.responseStatusCode != 404))) {
418                             return;
419                         } else if (userCatalogRequest.responseStatusCode == 200) {
420                             // Check if all users exist.
421                             NSDictionary *displaynameCatalog = [userCatalogRequest displaynameCatalog];
422                             NSMutableArray *inexistentGroupUsers = [NSMutableArray array];
423                             for (NSString *groupUser in allGroupUsers) {
424                                 if (![displaynameCatalog objectForKey:groupUser]) {
425                                     [inexistentGroupUsers addObject:groupUser];
426                                 }
427                             }
428                             if (!inexistentGroupUsers.count) {
429                                 // Create groups.
430                                 for (NSString *groupName in translatedGroups) {
431                                     NSMutableArray *groupUsers = [NSMutableArray array];
432                                     for (NSString *groupUser in [translatedGroups objectForKey:groupName]) {
433                                         [groupUsers addObject:([groupUser isEqualToString:@"*"] ?
434                                                                @"*" : [displaynameCatalog objectForKey:groupUser])];
435                                     }
436                                     [groups setObject:groupUsers forKey:groupName];
437                                 }
438                             } else {
439                                 NSAlert *alert = [[NSAlert alloc] init];
440                                 if (inexistentGroupUsers.count == 1) {
441                                     [alert setMessageText:@"Invalid User"];
442                                     [alert setInformativeText:[NSString stringWithFormat:@"User '%@' doesn't exist.", [inexistentGroupUsers objectAtIndex:0]]];
443                                 } else {
444                                     [alert setMessageText:@"Invalid Users"];
445                                     [alert setInformativeText:[NSString stringWithFormat:@"Users '%@' don't exist.", [inexistentGroupUsers componentsJoinedByString:@"', '"]]];
446                                 }
447                                 [alert addButtonWithTitle:@"OK"];
448                                 [alert runModal];
449                                 return;
450                             }
451                         } else {
452                             // 404. Since we don't translate to UUIDs, check for invalid chars.
453                             BOOL valid = YES;
454                             for (NSString *groupUser in allGroupUsers) {
455                                 if ([groupUser rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@" ~,;:"]].location != NSNotFound) {
456                                     valid = NO;
457                                     break;
458                                 }
459                             }
460                             if (valid) {
461                                 [groups addEntriesFromDictionary:translatedGroups];
462                             } else {
463                                 NSAlert *alert = [[NSAlert alloc] init];
464                                 [alert setMessageText:@"Invalid Input"];
465                                 [alert setInformativeText:@"Users cannot contain ' ', '~', ',', ';' or ':'."];
466                                 [alert addButtonWithTitle:@"OK"];
467                                 [alert runModal];
468                                 return;
469                             }
470                         }
471                     } else {
472                         for (NSString *groupName in translatedGroups) {
473                             if ([[translatedGroups objectForKey:groupName] containsObject:@"*"]) {
474                                 [groups setObject:[NSMutableArray arrayWithObject:@"*"] forKey:groupName];
475                             }
476                         }
477                         if (!groups.count) {
478                             [groups setObject:[NSArray arrayWithObject:@""] forKey:@"group"];
479                         }
480                     }
481                 } else {
482                     [groups addEntriesFromDictionary:translatedGroups];
483                 }
484             } else {
485                 [groups setObject:[NSArray arrayWithObject:@""] forKey:@"group"];
486             }
487             
488             self.applyMetadataAccountRequest = [ASIPithosAccountRequest updateAccountMetadataRequestWithPithos:pithos
489                                                                                                         groups:groups
490                                                                                                       metadata:pithosAccount.metadata
491                                                                                                         update:NO];
492             applyMetadataAccountRequest.delegate = self;
493             applyMetadataAccountRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
494             applyMetadataAccountRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
495             applyMetadataAccountRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
496                                                     [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
497                                                     [NSNumber numberWithUnsignedInteger:10], @"retries", 
498                                                     NSStringFromSelector(@selector(accountMetadataRequestFinished:)), @"didFinishSelector", 
499                                                     NSStringFromSelector(@selector(accountMetadataRequestFailed:)), @"didFailSelector", 
500                                                     nil];
501             [[PithosUtilities prepareRequest:applyMetadataAccountRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
502         }
503     }
504 }
505
506 - (void)refreshInfo {
507     @synchronized(self) {
508         if (refreshMetadataAccountRequest == nil) {
509             self.refreshMetadataAccountRequest = [ASIPithosAccountRequest accountMetadataRequestWithPithos:pithos];
510             refreshMetadataAccountRequest.delegate = self;
511             refreshMetadataAccountRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
512             refreshMetadataAccountRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
513             refreshMetadataAccountRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
514                                                       [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
515                                                       [NSNumber numberWithUnsignedInteger:10], @"retries", 
516                                                       NSStringFromSelector(@selector(accountMetadataRequestFinished:)), @"didFinishSelector", 
517                                                       NSStringFromSelector(@selector(accountMetadataRequestFailed:)), @"didFailSelector", 
518                                                       nil];
519             if (!sharingAccount)
520                 refreshMetadataAccountRequest.downloadCache = [ASIDownloadCache sharedCache];
521             [[PithosUtilities prepareRequest:refreshMetadataAccountRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
522         }
523     }
524 }
525
526 @end