5 // Copyright 2011-2012 GRNET S.A. All rights reserved.
7 // Redistribution and use in source and binary forms, with or
8 // without modification, are permitted provided that the following
11 // 1. Redistributions of source code must retain the above
12 // copyright notice, this list of conditions and the following
15 // 2. Redistributions in binary form must reproduce the above
16 // copyright notice, this list of conditions and the following
17 // disclaimer in the documentation and/or other materials
18 // provided with the distribution.
20 // THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
21 // OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
24 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
27 // USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28 // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 // POSSIBILITY OF SUCH DAMAGE.
33 // The views and conclusions contained in the software and
34 // documentation are those of the authors and should not be
35 // interpreted as representing official policies, either expressed
36 // or implied, of GRNET S.A.
38 #import "PithosSubdirNode.h"
39 #import "ASIPithosRequest.h"
41 #import "ASIPithosObjectRequest.h"
42 #import "ASIPithosContainer.h"
43 #import "ASIPithosObject.h"
44 #import "ASINetworkQueue.h"
45 #import "ASIDownloadCache.h"
46 #import "PithosUtilities.h"
47 #import "PithosObjectNodeInfoController.h"
49 static NSImage *sharedIcon = nil;
51 @implementation PithosSubdirNode
52 @synthesize pithosObject, versions;
56 if (self == [PithosSubdirNode class])
57 sharedIcon = [[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericFolderIcon)] retain];
61 #pragma mark Object Lifecycle
63 - (id)initWithPithos:(ASIPithos *)aPithos
64 pithosContainer:(ASIPithosContainer *)aPithosContainer
65 pithosObject:(ASIPithosObject *)aPithosObject {
66 if ((self = [super init])) {
67 self.pithos = aPithos;
68 self.pithosContainer = aPithosContainer;
69 self.pithosObject = aPithosObject;
76 [refreshVersionsObjectRequest clearDelegatesAndCancel];
77 [refreshVersionsObjectRequest release];
78 [refreshMetadataObjectRequest clearDelegatesAndCancel];
79 [refreshMetadataObjectRequest release];
80 [applyMetadataObjectRequest clearDelegatesAndCancel];
81 [applyMetadataObjectRequest release];
83 [pithosObject release];
88 #pragma mark Properties
90 - (void)setPithos:(ASIPithos *)aPithos {
91 if (aPithos && ![aPithos isEqualTo:pithos]) {
93 pithos = [aPithos retain];
101 url = [[NSString alloc] initWithFormat:@"subdir %@/%@/%@%@",
102 (sharingAccount ? [pithos storageURLWithAuthUser:sharingAccount] : pithos.storageURL),
103 pithosContainer.name,
105 (shared ? @"?shared" : @"")];
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];
116 return [[displayName copy] autorelease];
119 - (void)setDisplayName:(NSString *)aDisplayName {
124 icon = [sharedIcon retain];
128 - (void)setPithosObject:(ASIPithosObject *)aPithosObject {
129 if (![pithosObject isEqualTo:aPithosObject]) {
130 [pithosObject release];
131 pithosObject = [aPithosObject retain];
133 if (pithosObject.subdir) {
134 self.prefix = [pithosObject.name substringToIndex:([pithosObject.name length] - 1)];
136 self.prefix = [NSString stringWithString:pithosObject.name];
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];
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;
169 #pragma mark ASIHTTPRequestDelegate
171 - (void)objectRequestFinished:(ASIPithosObjectRequest *)request {
173 DLog(@"URL: %@", [request url]);
174 DLog(@"cached: %d", [request didUseCachedResponse]);
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;
184 if ((responseStatusCode == 201) || (responseStatusCode == 202))
186 } else if ([request isEqualTo:refreshMetadataObjectRequest]) {
187 [[pithosNodeInfoController window] makeFirstResponder:nil];
188 self.pithosObject = [refreshMetadataObjectRequest object];
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
195 @synchronized(self) {
196 [refreshMetadataObjectRequest release];
197 refreshMetadataObjectRequest = nil;
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;
210 - (void)objectRequestFailed:(ASIPithosObjectRequest *)request {
212 NSUInteger retries = [[request.userInfo objectForKey:@"retries"] unsignedIntegerValue];
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;
221 } else if ([request isEqualTo:refreshMetadataObjectRequest]) {
222 @synchronized(self) {
223 [refreshMetadataObjectRequest release];
224 refreshMetadataObjectRequest = newRequest;
226 } else if ([request isEqualTo:refreshVersionsObjectRequest]) {
227 @synchronized(self) {
228 [refreshVersionsObjectRequest release];
229 refreshVersionsObjectRequest = newRequest;
232 [[PithosUtilities prepareRequest:newRequest priority:[[newRequest.userInfo objectForKey:@"priority"] integerValue]] startAsynchronous];
233 [newRequest release];
235 if ([request isEqualTo:applyMetadataObjectRequest]) {
236 [PithosUtilities httpRequestErrorAlertWithRequest:applyMetadataObjectRequest];
237 @synchronized(self) {
238 [applyMetadataObjectRequest release];
239 applyMetadataObjectRequest = nil;
241 } else if ([request isEqualTo:refreshMetadataObjectRequest]) {
242 [PithosUtilities httpRequestErrorAlertWithRequest:refreshMetadataObjectRequest];
243 @synchronized(self) {
244 [refreshMetadataObjectRequest release];
245 refreshMetadataObjectRequest = nil;
247 } else if ([request isEqualTo:refreshVersionsObjectRequest]) {
248 [PithosUtilities httpRequestErrorAlertWithRequest:refreshVersionsObjectRequest];
249 @synchronized(self) {
250 [refreshVersionsObjectRequest release];
251 refreshVersionsObjectRequest = nil;
262 @synchronized(self) {
263 if (applyMetadataObjectRequest == nil) {
264 if (pithosObject.subdir) {
265 BOOL createObject = NO;
267 ASIPithosObjectRequest *request = [ASIPithosObjectRequest objectMetadataRequestWithPithos:pithos
268 containerName:pithosContainer.name
270 ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
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"];
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
291 ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
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"];
301 } else if (request.responseStatusCode != 204) {
302 [PithosUtilities unexpectedResponseStatusAlertWithRequest:request];
310 } else if (request.responseStatusCode == 404) {
313 [PithosUtilities unexpectedResponseStatusAlertWithRequest:request];
317 [[pithosNodeInfoController window] makeFirstResponder:nil];
318 applyMetadataObjectRequest = [[ASIPithosObjectRequest writeObjectDataRequestWithPithos:pithos
319 containerName:pithosContainer.name
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",
340 [[PithosUtilities prepareRequest:applyMetadataObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
343 [[pithosNodeInfoController window] makeFirstResponder:nil];
344 if (sharingAccount) {
345 applyMetadataObjectRequest = [[ASIPithosObjectRequest updateObjectMetadataRequestWithPithos:pithos
346 containerName:pithosContainer.name
347 objectName:pithosObject.name
349 contentDisposition:nil
352 isPublic:(isPublic ? ASIPithosObjectRequestPublicTrue : ASIPithosObjectRequestPublicFalse)
353 metadata:pithosObject.metadata
355 [applyMetadataObjectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
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
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",
377 [[PithosUtilities prepareRequest:applyMetadataObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
383 - (void)refreshInfo {
384 @synchronized(self) {
385 if (pithosObject.subdir) {
386 self.pithosObject = [ASIPithosObject subdirWithName:pithosObject.name];
388 } else if (refreshMetadataObjectRequest == nil) {
389 refreshMetadataObjectRequest = [[ASIPithosObjectRequest objectMetadataRequestWithPithos:pithos
390 containerName:pithosContainer.name
391 objectName:prefix] retain];
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",
404 refreshMetadataObjectRequest.downloadCache = [ASIDownloadCache sharedCache];
405 [[PithosUtilities prepareRequest:refreshMetadataObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
408 [self refreshVersions];
412 #pragma mark Versions
414 - (void)refreshVersions {
415 @synchronized(self) {
416 if (pithosObject.subdir) {
418 } else if (refreshVersionsObjectRequest == nil) {
419 refreshVersionsObjectRequest = [[ASIPithosObjectRequest objectVersionsRequestWithPithos:pithos
420 containerName:pithosContainer.name
421 objectName:pithosObject.name] retain];
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",
434 refreshVersionsObjectRequest.downloadCache = [ASIDownloadCache sharedCache];
435 [[PithosUtilities prepareRequest:refreshVersionsObjectRequest priority:NSOperationQueuePriorityHigh] startAsynchronous];
443 - (void)showPithosNodeInfo:(id)sender {
444 if (!pithosNodeInfoController) {
445 pithosNodeInfoController = [[PithosObjectNodeInfoController alloc] initWithPithosNode:self];
448 [pithosNodeInfoController showWindow:sender];
449 [[pithosNodeInfoController window] makeKeyAndOrderFront:sender];
450 [NSApp activateIgnoringOtherApps:YES];