Fix analyze errors
[pithos-macos] / pithos-macos / PithosSubdirNode.m
1 //
2 //  PithosSubdirNode.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 "PithosSubdirNode.h"
39 #import "ASIPithosRequest.h"
40 #import "ASIPithos.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"
48
49 static NSImage *sharedIcon = nil;
50
51 @implementation PithosSubdirNode
52 @synthesize pithosObject, versions;
53 @synthesize isPublic;
54
55 + (void)initialize {
56         if (self == [PithosSubdirNode class])
57         sharedIcon = [[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericFolderIcon)] retain];
58 }
59
60 #pragma mark -
61 #pragma mark Object Lifecycle
62
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         refreshParent = NO;
71     }
72     return self;
73 }
74
75 - (void)dealloc {
76     [refreshVersionsObjectRequest clearDelegatesAndCancel];
77     [refreshVersionsObjectRequest release];
78     [refreshMetadataObjectRequest clearDelegatesAndCancel];
79     [refreshMetadataObjectRequest release];
80     [applyMetadataObjectRequest clearDelegatesAndCancel];
81     [applyMetadataObjectRequest release];
82     [versions release];
83     [pithosObject release];
84     [super dealloc];
85 }
86
87 #pragma mark -
88 #pragma mark Properties
89
90 - (void)setPithos:(ASIPithos *)aPithos {
91     if (aPithos && ![aPithos isEqualTo:pithos]) {
92         [pithos release];
93         pithos = [aPithos retain];
94         [url release];
95         url = nil;
96     }
97 }
98
99 - (NSString *)url {
100     if (url == nil)
101         url = [[NSString alloc] initWithFormat:@"subdir %@/%@/%@%@", 
102                (sharingAccount ? [pithos storageURLWithAuthUser:sharingAccount] : pithos.storageURL), 
103                pithosContainer.name, 
104                prefix, 
105                (shared ? @"?shared" : @"")];
106     return url;
107 }
108
109 - (NSString *)displayName {
110     if (displayName == nil) {
111         displayName = [pithosObject.name lastPathComponent];
112         if (!pithosObject.subdir && [pithosObject.name hasSuffix:@"/"])
113             displayName = [displayName stringByAppendingString:@"/"];
114         [displayName retain];
115     } 
116     return [[displayName copy] autorelease];
117 }
118
119 - (void)setDisplayName:(NSString *)aDisplayName {    
120 }
121
122 - (NSImage *)icon {
123     if (icon == nil)
124         icon = [sharedIcon retain];
125     return icon;
126 }
127
128 - (void)setPithosObject:(ASIPithosObject *)aPithosObject {
129     if (![pithosObject isEqualTo:aPithosObject]) {
130         [pithosObject release];
131         pithosObject = [aPithosObject retain];
132     }
133     if (pithosObject.subdir) {
134         self.prefix = [pithosObject.name substringToIndex:([pithosObject.name length] - 1)];
135     } else {
136         self.prefix = [NSString stringWithString:pithosObject.name];
137     }
138     self.isPublic = (pithosObject.publicURI != nil);
139     // Refresh browser if the object is in my shared and is no longer shared
140     if (shared && !pithosObject.subdir && !pithosObject.sharing) {
141         [[NSNotificationCenter defaultCenter] postNotificationName:@"PithosBrowserRefreshNeeeded" object:self];
142     }
143 }
144
145 - (void)setLimitedPithosObject:(ASIPithosObject *)aPithosObject {
146     if (![pithosObject isEqualTo:aPithosObject]) {
147         self.pithosObject.subdir = aPithosObject.subdir;
148         self.pithosObject.name = aPithosObject.name;
149         self.pithosObject.hash = aPithosObject.hash;
150         self.pithosObject.objectHash = aPithosObject.objectHash;
151         self.pithosObject.UUID = aPithosObject.UUID;
152         self.pithosObject.bytes = aPithosObject.bytes;
153         self.pithosObject.contentType = aPithosObject.contentType;
154         self.pithosObject.lastModified = aPithosObject.lastModified;
155         self.pithosObject.version = aPithosObject.version;
156         self.pithosObject.versionTimestamp = aPithosObject.versionTimestamp;
157         self.pithosObject.modifiedBy = aPithosObject.modifiedBy;
158         self.pithosObject.sharedBy = aPithosObject.sharedBy;
159         self.pithosObject.allowedTo = aPithosObject.allowedTo;
160         if (!pithosNodeInfoController) {
161             self.pithosObject.sharing = aPithosObject.sharing;
162             self.pithosObject.publicURI = aPithosObject.publicURI;
163             self.pithosObject = pithosObject;
164         }
165     }
166 }
167
168 #pragma mark -
169 #pragma mark ASIHTTPRequestDelegate
170
171 - (void)objectRequestFinished:(ASIPithosObjectRequest *)request {
172     @autoreleasepool {
173         DLog(@"URL: %@", [request url]);
174         DLog(@"cached: %d", [request didUseCachedResponse]);
175         
176         if ([request isEqualTo:applyMetadataObjectRequest]) {
177             int responseStatusCode = applyMetadataObjectRequest.responseStatusCode;
178             if ((responseStatusCode != 201) && (responseStatusCode != 202))
179                 [PithosUtilities unexpectedResponseStatusAlertWithRequest:applyMetadataObjectRequest];
180             @synchronized(self) {
181                 [applyMetadataObjectRequest release];
182                 applyMetadataObjectRequest = nil;
183             }
184             if ((responseStatusCode == 201) || (responseStatusCode == 202))
185                 [self refreshInfo];
186         } else if ([request isEqualTo:refreshMetadataObjectRequest]) {
187             [[pithosNodeInfoController window] makeFirstResponder:nil];
188             self.pithosObject = [refreshMetadataObjectRequest object];
189             if (refreshParent) {
190                 // Ask the parent for refresh for the case where an object was removed
191                 // It is done here so that it doesn't affect the info window refresh
192                 [parent refresh];
193                 refreshParent = NO;
194             }
195             @synchronized(self) {
196                 [refreshMetadataObjectRequest release];
197                 refreshMetadataObjectRequest = nil;
198             }
199         } else if ([request isEqualTo:refreshVersionsObjectRequest]) {
200             [[pithosNodeInfoController window] makeFirstResponder:nil];
201             self.versions = [refreshVersionsObjectRequest versions];
202             @synchronized(self) {
203                 [refreshVersionsObjectRequest release];
204                 refreshVersionsObjectRequest = nil;
205             }
206         }
207     }
208 }
209
210 - (void)objectRequestFailed:(ASIPithosObjectRequest *)request {
211     @autoreleasepool {
212         NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue];
213         if (retries > 0) {
214             ASIPithosObjectRequest *newRequest = (ASIPithosObjectRequest *)[PithosUtilities copyRequest:request];
215             [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
216             if ([request isEqualTo:applyMetadataObjectRequest]) {
217                 @synchronized(self) {
218                     [applyMetadataObjectRequest release];
219                     applyMetadataObjectRequest = newRequest;
220                 }
221             } else if ([request isEqualTo:refreshMetadataObjectRequest]) {
222                 @synchronized(self) {
223                     [refreshMetadataObjectRequest release];
224                     refreshMetadataObjectRequest = newRequest;
225                 }
226             } else if ([request isEqualTo:refreshVersionsObjectRequest]) {
227                 @synchronized(self) {
228                     [refreshVersionsObjectRequest release];
229                     refreshVersionsObjectRequest = newRequest;
230                 }
231             }
232             [[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous];
233             [newRequest release];
234         } else {
235             if ([request isEqualTo:applyMetadataObjectRequest]) {
236                 [PithosUtilities httpRequestErrorAlertWithRequest:applyMetadataObjectRequest];
237                 @synchronized(self) {
238                     [applyMetadataObjectRequest release];
239                     applyMetadataObjectRequest = nil;
240                 }
241             } else if ([request isEqualTo:refreshMetadataObjectRequest]) {
242                 [PithosUtilities httpRequestErrorAlertWithRequest:refreshMetadataObjectRequest];
243                 @synchronized(self) {
244                     [refreshMetadataObjectRequest release];
245                     refreshMetadataObjectRequest = nil;
246                 }
247             } else if ([request isEqualTo:refreshVersionsObjectRequest]) {
248                 [PithosUtilities httpRequestErrorAlertWithRequest:refreshVersionsObjectRequest];
249                 @synchronized(self) {
250                     [refreshVersionsObjectRequest release];
251                     refreshVersionsObjectRequest = nil;
252                 }
253             }
254         }
255     }
256 }
257
258 #pragma mark -
259 #pragma mark Info
260
261 - (void)applyInfo {
262     @synchronized(self) {
263         if (applyMetadataObjectRequest == nil) {
264             if (pithosObject.subdir) {
265                 BOOL createObject = NO;
266                 NSAlert *alert;
267                 ASIPithosObjectRequest *request = [ASIPithosObjectRequest objectMetadataRequestWithPithos:pithos 
268                                                                                             containerName:pithosContainer.name 
269                                                                                                objectName:prefix];
270                 ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
271                 [networkQueue go];
272                 [networkQueue addOperations:[NSArray arrayWithObject:[PithosUtilities prepareRequest:request]] waitUntilFinished:YES];
273                 if ([request error]) {
274                     alert = [[[NSAlert alloc] init] autorelease];
275                     [alert setMessageText:@"HTTP Request Error"];
276                     [alert setInformativeText:[NSString stringWithFormat:@"An error occured: %@", [[request error] localizedDescription]]];
277                     [alert addButtonWithTitle:@"OK"];
278                     [alert runModal];
279                     return;
280                 } else if (request.responseStatusCode == 200) {
281                     alert = [[[NSAlert alloc] init] autorelease];
282                     [alert setMessageText:@"Apply changes"];
283                     [alert setInformativeText:[NSString stringWithFormat:@"In order to apply the changes in '%@', the object at the same path must be replaced. Continue?", self.displayName]];
284                     [alert addButtonWithTitle:@"OK"];
285                     [alert addButtonWithTitle:@"Cancel"];
286                     NSInteger choice = [alert runModal];
287                     if (choice == NSAlertFirstButtonReturn) {
288                         request = [ASIPithosObjectRequest deleteObjectRequestWithPithos:pithos 
289                                                                           containerName:pithosContainer.name 
290                                                                              objectName:prefix];
291                         ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
292                         [networkQueue go];
293                         [networkQueue addOperations:[NSArray arrayWithObject:[PithosUtilities prepareRequest:request]] waitUntilFinished:YES];
294                         if ([request error]) {
295                             alert = [[[NSAlert alloc] init] autorelease];
296                             [alert setMessageText:@"HTTP Request Error"];
297                             [alert setInformativeText:[NSString stringWithFormat:@"An error occured: %@", [[request error] localizedDescription]]];
298                             [alert addButtonWithTitle:@"OK"];
299                             [alert runModal];
300                             return;
301                         } else if (request.responseStatusCode != 204) {
302                             [PithosUtilities unexpectedResponseStatusAlertWithRequest:request];
303                             return;
304                         }
305                         refreshParent = YES;
306                         createObject = YES;
307                     } else {
308                         return;
309                     }
310                 } else if (request.responseStatusCode == 404) {
311                     createObject = YES;
312                 } else {
313                     [PithosUtilities unexpectedResponseStatusAlertWithRequest:request];
314                     return;
315                 }
316                 if (createObject) {
317                     [[pithosNodeInfoController window] makeFirstResponder:nil];
318                     applyMetadataObjectRequest = [[ASIPithosObjectRequest writeObjectDataRequestWithPithos:pithos 
319                                                                                              containerName:pithosContainer.name 
320                                                                                                 objectName:prefix 
321                                                                                                       eTag:nil
322                                                                                                contentType:@"application/directory"
323                                                                                            contentEncoding:pithosObject.contentEncoding 
324                                                                                         contentDisposition:pithosObject.contentDisposition 
325                                                                                                   manifest:pithosObject.manifest 
326                                                                                                    sharing:pithosObject.sharing 
327                                                                                                   isPublic:(isPublic ? ASIPithosObjectRequestPublicTrue : ASIPithosObjectRequestPublicFalse) 
328                                                                                                   metadata:pithosObject.metadata 
329                                                                                                       data:[NSData data]] retain];
330                     pithosObject.subdir = NO;
331                     applyMetadataObjectRequest.delegate = self;
332                     applyMetadataObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
333                     applyMetadataObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
334                     applyMetadataObjectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
335                                                            [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
336                                                            [NSNumber numberWithUnsignedInteger:10], @"retries", 
337                                                            NSStringFromSelector(@selector(objectRequestFinished:)), @"didFinishSelector", 
338                                                            NSStringFromSelector(@selector(objectRequestFailed:)), @"didFailSelector", 
339                                                            nil];
340                     [[PithosUtilities prepareRequest:applyMetadataObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
341                 }
342             } else {
343                 [[pithosNodeInfoController window] makeFirstResponder:nil];
344                 if (sharingAccount) {
345                     applyMetadataObjectRequest = [[ASIPithosObjectRequest updateObjectMetadataRequestWithPithos:pithos 
346                                                                                                   containerName:pithosContainer.name 
347                                                                                                      objectName:pithosObject.name 
348                                                                                                 contentEncoding:nil
349                                                                                              contentDisposition:nil
350                                                                                                        manifest:nil 
351                                                                                                         sharing:nil 
352                                                                                                        isPublic:(isPublic ? ASIPithosObjectRequestPublicTrue : ASIPithosObjectRequestPublicFalse) 
353                                                                                                        metadata:pithosObject.metadata
354                                                                                                          update:NO] retain];
355                     [applyMetadataObjectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
356                 } else {
357                     applyMetadataObjectRequest = [[ASIPithosObjectRequest updateObjectMetadataRequestWithPithos:pithos 
358                                                                                                   containerName:pithosContainer.name 
359                                                                                                      objectName:pithosObject.name 
360                                                                                                 contentEncoding:pithosObject.contentEncoding
361                                                                                              contentDisposition:pithosObject.contentDisposition 
362                                                                                                        manifest:pithosObject.manifest 
363                                                                                                         sharing:(pithosObject.sharing ? pithosObject.sharing : @"") 
364                                                                                                        isPublic:(isPublic ? ASIPithosObjectRequestPublicTrue : ASIPithosObjectRequestPublicFalse) 
365                                                                                                        metadata:pithosObject.metadata
366                                                                                                          update:NO] retain];
367                 }
368                 applyMetadataObjectRequest.delegate = self;
369                 applyMetadataObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
370                 applyMetadataObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
371                 applyMetadataObjectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
372                                                        [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
373                                                        [NSNumber numberWithUnsignedInteger:10], @"retries", 
374                                                        NSStringFromSelector(@selector(objectRequestFinished:)), @"didFinishSelector", 
375                                                        NSStringFromSelector(@selector(objectRequestFailed:)), @"didFailSelector", 
376                                                        nil];
377                 [[PithosUtilities prepareRequest:applyMetadataObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
378             }
379         }
380     }
381 }
382
383 - (void)refreshInfo {
384     @synchronized(self) {
385         if (pithosObject.subdir) {
386             self.pithosObject = [ASIPithosObject subdirWithName:pithosObject.name];
387             return;
388         } else if (refreshMetadataObjectRequest == nil) {
389             refreshMetadataObjectRequest = [[ASIPithosObjectRequest objectMetadataRequestWithPithos:pithos 
390                                                                                       containerName:pithosContainer.name 
391                                                                                          objectName:prefix] retain];
392             if (sharingAccount)
393                 [refreshMetadataObjectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
394             refreshMetadataObjectRequest.delegate = self;
395             refreshMetadataObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
396             refreshMetadataObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
397             refreshMetadataObjectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
398                                                      [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
399                                                      [NSNumber numberWithUnsignedInteger:10], @"retries", 
400                                                      NSStringFromSelector(@selector(objectRequestFinished:)), @"didFinishSelector", 
401                                                      NSStringFromSelector(@selector(objectRequestFailed:)), @"didFailSelector", 
402                                                      nil];
403             if (!sharingAccount)
404                 refreshMetadataObjectRequest.downloadCache = [ASIDownloadCache sharedCache];
405             [[PithosUtilities prepareRequest:refreshMetadataObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
406         }
407     }
408     [self refreshVersions];
409 }
410
411 #pragma mark -
412 #pragma mark Versions
413
414 - (void)refreshVersions {
415     @synchronized(self) {
416         if (pithosObject.subdir) {
417             return;
418         } else if (refreshVersionsObjectRequest == nil) {
419             refreshVersionsObjectRequest = [[ASIPithosObjectRequest objectVersionsRequestWithPithos:pithos 
420                                                                                       containerName:pithosContainer.name 
421                                                                                          objectName:pithosObject.name] retain];
422             if (sharingAccount)
423                 [refreshVersionsObjectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
424             refreshVersionsObjectRequest.delegate = self;
425             refreshVersionsObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
426             refreshVersionsObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
427             refreshVersionsObjectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
428                                                      [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority", 
429                                                      [NSNumber numberWithUnsignedInteger:10], @"retries", 
430                                                      NSStringFromSelector(@selector(objectRequestFinished:)), @"didFinishSelector", 
431                                                      NSStringFromSelector(@selector(objectRequestFailed:)), @"didFailSelector", 
432                                                      nil];
433             if (!sharingAccount)
434                 refreshVersionsObjectRequest.downloadCache = [ASIDownloadCache sharedCache];
435             [[PithosUtilities prepareRequest:refreshVersionsObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
436         }
437     }
438 }
439
440 #pragma mark -
441 #pragma mark Actions
442
443 - (void)showPithosNodeInfo:(id)sender {
444     if (!pithosNodeInfoController) {
445         pithosNodeInfoController = [[PithosObjectNodeInfoController alloc] initWithPithosNode:self];
446         [self refreshInfo];
447     }
448     [pithosNodeInfoController showWindow:sender];
449     [[pithosNodeInfoController window] makeKeyAndOrderFront:sender];
450     [NSApp activateIgnoringOtherApps:YES];
451 }
452
453 @end