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;
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;
76 [refreshMetadataObjectRequest clearDelegatesAndCancel];
77 [refreshMetadataObjectRequest release];
78 [applyMetadataObjectRequest clearDelegatesAndCancel];
79 [applyMetadataObjectRequest release];
80 [pithosObject release];
85 #pragma mark Properties
87 - (void)setPithos:(ASIPithos *)aPithos {
88 if (aPithos && ![aPithos isEqualTo:pithos]) {
90 pithos = [aPithos retain];
98 url = [[NSString alloc] initWithFormat:@"subdir %@/%@/%@%@",
99 (sharingAccount ? [pithos storageURLWithAuthUser:sharingAccount] : pithos.storageURL),
100 pithosContainer.name,
102 (shared ? @"?shared" : @"")];
106 - (NSString *)displayName {
107 if (displayName == nil) {
108 displayName = [pithosObject.name lastPathComponent];
109 if (!pithosObject.subdir && [pithosObject.name hasSuffix:@"/"])
110 displayName = [displayName stringByAppendingString:@"/"];
111 [displayName retain];
113 return [[displayName copy] autorelease];
116 - (void)setDisplayName:(NSString *)aDisplayName {
121 icon = [sharedIcon retain];
125 - (void)setPithosObject:(ASIPithosObject *)aPithosObject {
126 if (![pithosObject isEqualTo:aPithosObject]) {
127 [pithosObject release];
128 pithosObject = [aPithosObject retain];
130 if (pithosObject.subdir) {
131 self.prefix = [pithosObject.name substringToIndex:([pithosObject.name length] - 1)];
133 self.prefix = [NSString stringWithString:pithosObject.name];
135 self.isPublic = (pithosObject.publicURI != nil);
136 // Refresh browser if the object is in my shared and is no longer shared
137 if (shared && !pithosObject.subdir && !pithosObject.sharing) {
138 [[NSNotificationCenter defaultCenter] postNotificationName:@"PithosBrowserRefreshNeeeded" object:self];
142 - (void)setLimitedPithosObject:(ASIPithosObject *)aPithosObject {
143 if (![pithosObject isEqualTo:aPithosObject]) {
144 self.pithosObject.subdir = aPithosObject.subdir;
145 self.pithosObject.name = aPithosObject.name;
146 self.pithosObject.hash = aPithosObject.hash;
147 self.pithosObject.objectHash = aPithosObject.objectHash;
148 self.pithosObject.UUID = aPithosObject.UUID;
149 self.pithosObject.bytes = aPithosObject.bytes;
150 self.pithosObject.contentType = aPithosObject.contentType;
151 self.pithosObject.lastModified = aPithosObject.lastModified;
152 self.pithosObject.version = aPithosObject.version;
153 self.pithosObject.versionTimestamp = aPithosObject.versionTimestamp;
154 self.pithosObject.modifiedBy = aPithosObject.modifiedBy;
155 self.pithosObject.sharedBy = aPithosObject.sharedBy;
156 self.pithosObject.allowedTo = aPithosObject.allowedTo;
157 if (!pithosNodeInfoController) {
158 self.pithosObject.sharing = aPithosObject.sharing;
159 self.pithosObject.publicURI = aPithosObject.publicURI;
160 self.pithosObject = pithosObject;
166 #pragma mark ASIHTTPRequestDelegate
168 - (void)objectRequestFinished:(ASIPithosObjectRequest *)request {
169 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
170 NSLog(@"URL: %@", [request url]);
171 NSLog(@"cached: %d", [request didUseCachedResponse]);
173 if ([request isEqualTo:applyMetadataObjectRequest]) {
174 int responseStatusCode = applyMetadataObjectRequest.responseStatusCode;
175 if ((responseStatusCode != 201) && (responseStatusCode != 202))
176 [PithosUtilities unexpectedResponseStatusAlertWithRequest:applyMetadataObjectRequest];
177 @synchronized(self) {
178 [applyMetadataObjectRequest release];
179 applyMetadataObjectRequest = nil;
181 if ((responseStatusCode == 201) || (responseStatusCode == 202))
183 } else if ([request isEqualTo:refreshMetadataObjectRequest]) {
184 [[pithosNodeInfoController window] makeFirstResponder:nil];
185 self.pithosObject = [refreshMetadataObjectRequest object];
187 // Ask the parent for refresh for the case where an object was removed
188 // It is done here so that it doesn't affect the info window refresh
192 @synchronized(self) {
193 [refreshMetadataObjectRequest release];
194 refreshMetadataObjectRequest = nil;
200 - (void)objectRequestFailed:(ASIPithosObjectRequest *)request {
201 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
202 NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue];
204 ASIPithosObjectRequest *newRequest = (ASIPithosObjectRequest *)[[PithosUtilities copyRequest:request] autorelease];
205 [(NSMutableDictionary *)(newRequest.userInfo)setObject:[NSNumber numberWithUnsignedInteger:(--retries)] forKey:@"retries"];
206 if ([request isEqualTo:applyMetadataObjectRequest]) {
207 @synchronized(self) {
208 [applyMetadataObjectRequest release];
209 applyMetadataObjectRequest = newRequest;
211 } else if ([request isEqualTo:refreshMetadataObjectRequest]) {
212 @synchronized(self) {
213 [refreshMetadataObjectRequest release];
214 refreshMetadataObjectRequest = newRequest;
217 [[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous];
219 if ([request isEqualTo:applyMetadataObjectRequest]) {
220 dispatch_async(dispatch_get_main_queue(), ^{
221 [PithosUtilities httpRequestErrorAlertWithRequest:applyMetadataObjectRequest];
223 @synchronized(self) {
224 [applyMetadataObjectRequest release];
225 applyMetadataObjectRequest = nil;
227 } else if ([request isEqualTo:refreshMetadataObjectRequest]) {
228 dispatch_async(dispatch_get_main_queue(), ^{
229 [PithosUtilities httpRequestErrorAlertWithRequest:refreshMetadataObjectRequest];
231 @synchronized(self) {
232 [refreshMetadataObjectRequest release];
233 refreshMetadataObjectRequest = nil;
244 @synchronized(self) {
245 if (applyMetadataObjectRequest == nil) {
246 if (pithosObject.subdir) {
247 BOOL createObject = NO;
249 ASIPithosObjectRequest *request = [ASIPithosObjectRequest objectMetadataRequestWithPithos:pithos
250 containerName:pithosContainer.name
252 ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
254 [networkQueue addOperations:[NSArray arrayWithObject:[PithosUtilities prepareRequest:request]] waitUntilFinished:YES];
255 if ([request error]) {
256 alert = [[[NSAlert alloc] init] autorelease];
257 [alert setMessageText:@"HTTP Request Error"];
258 [alert setInformativeText:[NSString stringWithFormat:@"An error occured: %@", [request error]]];
259 [alert addButtonWithTitle:@"OK"];
262 } else if (request.responseStatusCode == 200) {
263 alert = [[[NSAlert alloc] init] autorelease];
264 [alert setMessageText:@"Apply changes"];
265 [alert setInformativeText:[NSString stringWithFormat:@"In order to apply the changes in '%@', the object at the same path must be replaced. Continue?", self.displayName]];
266 [alert addButtonWithTitle:@"OK"];
267 [alert addButtonWithTitle:@"Cancel"];
268 NSInteger choice = [alert runModal];
269 if (choice == NSAlertFirstButtonReturn) {
270 request = [ASIPithosObjectRequest deleteObjectRequestWithPithos:pithos
271 containerName:pithosContainer.name
273 ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
275 [networkQueue addOperations:[NSArray arrayWithObject:[PithosUtilities prepareRequest:request]] waitUntilFinished:YES];
276 if ([request error]) {
277 alert = [[[NSAlert alloc] init] autorelease];
278 [alert setMessageText:@"HTTP Request Error"];
279 [alert setInformativeText:[NSString stringWithFormat:@"An error occured: %@", [request error]]];
280 [alert addButtonWithTitle:@"OK"];
283 } else if (request.responseStatusCode != 204) {
284 [PithosUtilities unexpectedResponseStatusAlertWithRequest:request];
292 } else if (request.responseStatusCode == 404) {
295 [PithosUtilities unexpectedResponseStatusAlertWithRequest:request];
299 [[pithosNodeInfoController window] makeFirstResponder:nil];
300 applyMetadataObjectRequest = [[ASIPithosObjectRequest writeObjectDataRequestWithPithos:pithos
301 containerName:pithosContainer.name
304 contentType:@"application/directory"
305 contentEncoding:pithosObject.contentEncoding
306 contentDisposition:pithosObject.contentDisposition
307 manifest:pithosObject.manifest
308 sharing:pithosObject.sharing
309 isPublic:(isPublic ? ASIPithosObjectRequestPublicTrue : ASIPithosObjectRequestPublicFalse)
310 metadata:pithosObject.metadata
311 data:[NSData data]] retain];
312 pithosObject.subdir = NO;
313 applyMetadataObjectRequest.delegate = self;
314 applyMetadataObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
315 applyMetadataObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
316 applyMetadataObjectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
317 [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority",
318 [NSNumber numberWithUnsignedInteger:10], @"retries",
319 NSStringFromSelector(@selector(objectRequestFinished:)), @"didFinishSelector",
320 NSStringFromSelector(@selector(objectRequestFailed:)), @"didFailSelector",
322 [[PithosUtilities prepareRequest:applyMetadataObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
325 [[pithosNodeInfoController window] makeFirstResponder:nil];
326 if (sharingAccount) {
327 applyMetadataObjectRequest = [[ASIPithosObjectRequest updateObjectMetadataRequestWithPithos:pithos
328 containerName:pithosContainer.name
329 objectName:pithosObject.name
331 contentDisposition:nil
334 isPublic:(isPublic ? ASIPithosObjectRequestPublicTrue : ASIPithosObjectRequestPublicFalse)
335 metadata:pithosObject.metadata
337 [applyMetadataObjectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
339 applyMetadataObjectRequest = [[ASIPithosObjectRequest updateObjectMetadataRequestWithPithos:pithos
340 containerName:pithosContainer.name
341 objectName:pithosObject.name
342 contentEncoding:pithosObject.contentEncoding
343 contentDisposition:pithosObject.contentDisposition
344 manifest:pithosObject.manifest
345 sharing:(pithosObject.sharing ? pithosObject.sharing : @"")
346 isPublic:(isPublic ? ASIPithosObjectRequestPublicTrue : ASIPithosObjectRequestPublicFalse)
347 metadata:pithosObject.metadata
350 applyMetadataObjectRequest.delegate = self;
351 applyMetadataObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
352 applyMetadataObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
353 applyMetadataObjectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
354 [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority",
355 [NSNumber numberWithUnsignedInteger:10], @"retries",
356 NSStringFromSelector(@selector(objectRequestFinished:)), @"didFinishSelector",
357 NSStringFromSelector(@selector(objectRequestFailed:)), @"didFailSelector",
359 [[PithosUtilities prepareRequest:applyMetadataObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
365 - (void)refreshInfo {
366 @synchronized(self) {
367 if (pithosObject.subdir) {
368 self.pithosObject = [ASIPithosObject subdirWithName:pithosObject.name];
369 } else if (refreshMetadataObjectRequest == nil) {
370 refreshMetadataObjectRequest = [[ASIPithosObjectRequest objectMetadataRequestWithPithos:pithos
371 containerName:pithosContainer.name
372 objectName:prefix] retain];
374 [refreshMetadataObjectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
375 refreshMetadataObjectRequest.delegate = self;
376 refreshMetadataObjectRequest.didFinishSelector = @selector(performRequestFinishedDelegateInBackground:);
377 refreshMetadataObjectRequest.didFailSelector = @selector(performRequestFailedDelegateInBackground:);
378 refreshMetadataObjectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
379 [NSNumber numberWithInteger:NSOperationQueuePriorityHigh], @"priority",
380 [NSNumber numberWithUnsignedInteger:10], @"retries",
381 NSStringFromSelector(@selector(objectRequestFinished:)), @"didFinishSelector",
382 NSStringFromSelector(@selector(objectRequestFailed:)), @"didFailSelector",
384 refreshMetadataObjectRequest.downloadCache = [ASIDownloadCache sharedCache];
385 [[PithosUtilities prepareRequest:refreshMetadataObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
393 - (void)showPithosNodeInfo:(id)sender {
394 if (!pithosNodeInfoController) {
395 pithosNodeInfoController = [[PithosObjectNodeInfoController alloc] initWithPithosNode:self];
398 [pithosNodeInfoController showWindow:sender];
399 [[pithosNodeInfoController window] makeKeyAndOrderFront:sender];
400 [NSApp activateIgnoringOtherApps:YES];