Initial implementation of drag and drop directory upload.
[pithos-macos] / pithos-macos / PithosBrowserController.m
index 64916ba..27ef4d3 100644 (file)
@@ -432,9 +432,9 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                 PithosNode *node = [browser itemAtRow:*row inColumn:*column];
                 if ([node class] != [PithosSubdirNode class])
                     *row = -1;
+            }
             *dropOperation = NSBrowserDropOn;
             result = NSDragOperationCopy;
-            }
         }
     }
     // XXX else local file promises
@@ -477,13 +477,7 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
                     dispatch_async(queue, ^{
                         NSError *error = nil;
-                        NSURLResponse *response = nil;
-                        [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:filePath] 
-                                                                                 cachePolicy:NSURLCacheStorageNotAllowed 
-                                                                             timeoutInterval:.1] 
-                                              returningResponse:&response 
-                                                          error:&error];
-                        NSString *contentType = [response MIMEType];
+                        NSString *contentType = [PithosFileUtilities contentTypeOfFile:filePath error:&error];
                         if (contentType == nil)
                             contentType = @"application/binary";
                         if (error)
@@ -498,7 +492,6 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                                                                                                checkIfExists:YES 
                                                                                                                       hashes:&hashes];
                         if (objectRequest) {
-                            // XXX set delegates and queue
                             objectRequest.delegate = self;
                             objectRequest.didFinishSelector = @selector(uploadObjectUsingHashMapFinished:);
                             objectRequest.didFailSelector = @selector(uploadObjectUsingHashMapFailed:);
@@ -510,17 +503,61 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                                       blockHash, @"blockHash", 
                                                       filePath, @"filePath", 
                                                       hashes, @"hashes", 
-                                                      [NSNumber numberWithBool:YES], @"checkIfExists", 
                                                       node, @"node", 
                                                       [NSNumber numberWithUnsignedInteger:0], @"iteration", 
                                                       nil];
                             [objectRequest startAsynchronous];
                         }
-                        // XXX else show alert?
                     });
                 } else {
                     // Upload directory, confirm first
-                    // XXX implement this
+                    NSAlert *alert = [[[NSAlert alloc] init] autorelease];
+                    [alert setMessageText:@"Upload directory"];
+                    [alert setInformativeText:[NSString stringWithFormat:@"'%@' is a directory, do you want to upload it and its contents?", filePath]];
+                    [alert addButtonWithTitle:@"OK"];
+                    [alert addButtonWithTitle:@"Cancel"];
+                    NSInteger choice = [alert runModal];
+                    if (choice == NSAlertFirstButtonReturn) {
+                        NSString *objectName = [objectNamePrefix stringByAppendingPathComponent:[filePath lastPathComponent]];
+                        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
+                        dispatch_async(queue, ^{
+                            NSMutableArray *objectNames = nil;
+                            NSMutableArray *contentTypes = nil;
+                            NSMutableArray *filePaths = nil;
+                            NSMutableArray *hashesArrays = nil;
+                            NSArray *objectRequests = [PithosFileUtilities writeObjectDataRequestsWithContainerName:containerName 
+                                                                                                         objectName:objectName 
+                                                                                                          blockSize:blockSize 
+                                                                                                          blockHash:blockHash 
+                                                                                                       forDirectory:filePath 
+                                                                                                      checkIfExists:YES 
+                                                                                                        objectNames:&objectNames
+                                                                                                       contentTypes:&contentTypes
+                                                                                                          filePaths:&filePaths
+                                                                                                         hashesArrays:&hashesArrays];
+                            if (objectRequests) {
+                                for (NSUInteger i = 0 ; i < [objectRequests count] ; i++) {
+                                    ASIPithosObjectRequest *objectRequest = [objectRequests objectAtIndex:i];
+                                    // XXX if dir creation requests differentiate
+                                    objectRequest.delegate = self;
+                                    objectRequest.didFinishSelector = @selector(uploadObjectUsingHashMapFinished:);
+                                    objectRequest.didFailSelector = @selector(uploadObjectUsingHashMapFailed:);
+                                    objectRequest.userInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+                                                              containerName, @"containerName", 
+                                                              [objectNames objectAtIndex:i], @"objectName", 
+                                                              [contentTypes objectAtIndex:i], @"contentType", 
+                                                              [NSNumber numberWithUnsignedInteger:blockSize], @"blockSize", 
+                                                              blockHash, @"blockHash", 
+                                                              [filePaths objectAtIndex:i], @"filePath", 
+                                                              [hashesArrays objectAtIndex:i], @"hashes", 
+                                                              [NSNull null], @"node", 
+                                                              [NSNumber numberWithUnsignedInteger:0], @"iteration", 
+                                                              nil];
+                                    [objectRequest startAsynchronous];
+                                }
+                            }
+                        });
+                    }
                 }
             }
             
@@ -535,16 +572,16 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
 #pragma mark ASIHTTPRequestDelegate
 
 - (void)downloadObjectFinished:(ASIPithosObjectRequest *)objectRequest {
-    NSLog(@"download completed: %@", [objectRequest url]);
+    NSLog(@"Download Completed: %@", [objectRequest url]);
     if ([objectRequest bytes] == 0) {
-        NSLog(@"downloaded  0 bytes");
+        NSLog(@"Downloaded  0 bytes");
         NSFileManager *defaultManager = [NSFileManager defaultManager];
         NSString *filePath = [objectRequest.userInfo objectForKey:@"filePath"];
         if (![defaultManager fileExistsAtPath:filePath]) {
             if (![defaultManager createFileAtPath:filePath contents:nil attributes:nil]) {
                 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
-                [alert setMessageText:@"File Creation Error"];
-                [alert setInformativeText:[NSString stringWithFormat:@"Couldn't create zero length file at %@", filePath]];
+                [alert setMessageText:@"Create File Error"];
+                [alert setInformativeText:[NSString stringWithFormat:@"Cannot create zero length file at %@", filePath]];
                 [alert addButtonWithTitle:@"OK"];
                 [alert runModal];
             }
@@ -553,26 +590,29 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
 }
 
 - (void)downloadObjectFailed:(ASIPithosObjectRequest *)objectRequest {
-    NSLog(@"download failed: %@, error: %@", [objectRequest url], [objectRequest error]);
-    NSAlert *alert = [[[NSAlert alloc] init] autorelease];
-    [alert setMessageText:@"HTTP Request Error"];
-    [alert setInformativeText:[NSString stringWithFormat:@"An error occured: %@", [objectRequest error]]];
-    [alert addButtonWithTitle:@"OK"];
-    [alert runModal];
+    NSLog(@"Download failed");
+    [PithosFileUtilities httpRequestErrorAlertWithRequest:objectRequest];
 }
 
 - (void)uploadObjectUsingHashMapFinished:(ASIPithosObjectRequest *)objectRequest {
-    NSLog(@"upload using hashmap completed: %@", [objectRequest url]);
+    NSLog(@"Upload using hashmap completed: %@", [objectRequest url]);
     if (objectRequest.responseStatusCode == 201) {
-        NSLog(@"object created: %@", [objectRequest url]);
+        NSLog(@"Object Created: %@", [objectRequest url]);
         PithosNode *node = [objectRequest.userInfo objectForKey:@"node"];
-        [node invalidateChildren];
-        node.children;
+        if (node != (id)[NSNull null]) {
+            [node invalidateChildren];
+            node.children;
+        }
     } else if (objectRequest.responseStatusCode == 409) {
         NSUInteger iteration = [[objectRequest.userInfo objectForKey:@"iteration"] unsignedIntegerValue] + 1;
         if (iteration > 10) {
-            NSLog(@"upload iteration limit reached: %@", [objectRequest url]);
-            // XXX show alert
+            NSLog(@"Upload iteration limit reached: %@", [objectRequest url]);
+            NSAlert *alert = [[[NSAlert alloc] init] autorelease];
+            [alert setMessageText:@"Upload Timeout"];
+            [alert setInformativeText:[NSString stringWithFormat:@"Upload iteration limit reached for object with path '%@'", 
+                                       [objectRequest.userInfo objectForKey:@"objectName"], [objectRequest.userInfo objectForKey:@"containerName"]]];
+            [alert addButtonWithTitle:@"OK"];
+            [alert runModal];        
             return;
         }
         NSLog(@"object is missing hashes: %@", [objectRequest url]);
@@ -582,36 +622,25 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
                                                                                                        blockSize:[[objectRequest.userInfo objectForKey:@"blockSize"] unsignedIntegerValue]
                                                                                                          forFile:[objectRequest.userInfo objectForKey:@"filePath"]
                                                                                                           hashes:[objectRequest.userInfo objectForKey:@"hashes"] 
-                                                                                           missingHashesResponse:[objectRequest responseString] 
-                                                                                                   checkIfExists:[[objectRequest.userInfo objectForKey:@"checkIfExists"] boolValue]];
-        newObjectRequest.shouldAttemptPersistentConnection = NO;
+                                                                                           missingHashesResponse:[objectRequest responseString]]; 
         newObjectRequest.delegate = self;
         newObjectRequest.didFinishSelector = @selector(uploadMissingHashesFinished:);
         newObjectRequest.didFailSelector = @selector(uploadMissingHashesFailed:);
         newObjectRequest.userInfo = objectRequest.userInfo;
-        [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:[NSNumber numberWithBool:NO] forKey:@"checkIfExists"];
         [(NSMutableDictionary *)(newObjectRequest.userInfo) setObject:[NSNumber numberWithUnsignedInteger:iteration] forKey:@"iteration"];
         [newObjectRequest startAsynchronous];
     } else {
-        NSAlert *alert = [[[NSAlert alloc] init] autorelease];
-        [alert setMessageText:@"Unexpected Response Status"];
-        [alert setInformativeText:[NSString stringWithFormat:@"Unexpected response status %d - %@", objectRequest.responseStatusCode, objectRequest.responseStatusMessage]];
-        [alert addButtonWithTitle:@"OK"];
-        [alert runModal];
+        [PithosFileUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
     }
 }
 
 - (void)uploadObjectUsingHashMapFailed:(ASIPithosObjectRequest *)objectRequest {
-    NSLog(@"upload failed: %@, error: %@", [objectRequest url], [objectRequest error]);
-    NSAlert *alert = [[[NSAlert alloc] init] autorelease];
-    [alert setMessageText:@"HTTP Request Error"];
-    [alert setInformativeText:[NSString stringWithFormat:@"An error occured: %@", [objectRequest error]]];
-    [alert addButtonWithTitle:@"OK"];
-    [alert runModal];
+    NSLog(@"Upload using hashmap failed");
+    [PithosFileUtilities httpRequestErrorAlertWithRequest:objectRequest];
 }
 
 - (void)uploadMissingHashesFinished:(ASIPithosObjectRequest *)objectRequest {
-    NSLog(@"upload of missing hashes completed: %@", [objectRequest url]);
+    NSLog(@"Upload of missing hashes completed: %@", [objectRequest url]);
     if ((objectRequest.responseStatusCode == 201) || (objectRequest.responseStatusCode == 204)) {
         NSArray *hashes = [objectRequest.userInfo objectForKey:@"hashes"];
         ASIPithosObjectRequest *newObjectRequest = [PithosFileUtilities writeObjectDataRequestWithContainerName:[objectRequest.userInfo objectForKey:@"containerName"] 
@@ -628,21 +657,13 @@ forDraggedRowsWithIndexes:(NSIndexSet *)rowIndexes inColumn:(NSInteger)column {
         newObjectRequest.userInfo = objectRequest.userInfo;
         [newObjectRequest startAsynchronous];
     } else {
-        NSAlert *alert = [[[NSAlert alloc] init] autorelease];
-        [alert setMessageText:@"Unexpected Response Status"];
-        [alert setInformativeText:[NSString stringWithFormat:@"Unexpected response status %d - %@", objectRequest.responseStatusCode, objectRequest.responseStatusMessage]];
-        [alert addButtonWithTitle:@"OK"];
-        [alert runModal];
+        [PithosFileUtilities unexpectedResponseStatusAlertWithRequest:objectRequest];
     }
 }
 
-- (void)uploadMissingHashesFailed:(ASIPithosObjectRequest *)objectRequest {    
-    NSLog(@"upload of missing hashes failed: %@, error: %@", [objectRequest url], [objectRequest error]);
-    NSAlert *alert = [[[NSAlert alloc] init] autorelease];
-    [alert setMessageText:@"HTTP Request Error"];
-    [alert setInformativeText:[NSString stringWithFormat:@"An error occured: %@", [objectRequest error]]];
-    [alert addButtonWithTitle:@"OK"];
-    [alert runModal];
+- (void)uploadMissingHashesFailed:(ASIPithosObjectRequest *)objectRequest {
+    NSLog(@"Upload of missing hashes failed");
+    [PithosFileUtilities httpRequestErrorAlertWithRequest:objectRequest];
 }
 
 #pragma mark -