Use user catalog in sync
[pithos-macos] / pithos-macos / PithosUtilities.m
index 7165508..d48928a 100644 (file)
     
     NSString *destinationPath = [directoryPath stringByAppendingPathComponent:fileName];
     if (ifExists && [fileManager fileExistsAtPath:destinationPath]) {
-        NSAlert *alert = [[[NSAlert alloc] init] autorelease];
-        [alert setMessageText:@"File Exists"];
-        [alert setInformativeText:[NSString stringWithFormat:@"A file or directory named '%@' already exists, do you want to replace it?", fileName]];
-        [alert addButtonWithTitle:@"OK"];
-        [alert addButtonWithTitle:@"Cancel"];
-        NSInteger choice = [alert runModal];
+        __block NSInteger choice;
+        dispatch_sync(dispatch_get_main_queue(), ^{
+            NSAlert *alert = [[NSAlert alloc] init];
+            [alert setMessageText:@"File Exists"];
+            [alert setInformativeText:[NSString stringWithFormat:@"A file or directory named '%@' already exists, do you want to replace it?", fileName]];
+            [alert addButtonWithTitle:@"OK"];
+            [alert addButtonWithTitle:@"Cancel"];
+            choice = [alert runModal];
+        });
         if (choice == NSAlertSecondButtonReturn)
             return nil;
     }
         [fileManager removeItemAtPath:directoryPath error:&error];
     }
     if (error) {
-        NSLog(@"Cannot remove existing file '%@': %@", fileName, error);
-        NSAlert *alert = [[[NSAlert alloc] init] autorelease];
-        [alert setMessageText:@"Removal Error"];
-        [alert setInformativeText:[NSString stringWithFormat:@"Cannot remove existing file '%@': %@", fileName, error]];
-        [alert addButtonWithTitle:@"OK"];
-        [alert runModal];
+        DLog(@"Cannot remove existing file '%@': %@", fileName, error);
+        dispatch_async(dispatch_get_main_queue(), ^{
+            NSAlert *alert = [[NSAlert alloc] init];
+            [alert setMessageText:@"Removal Error"];
+            [alert setInformativeText:[NSString stringWithFormat:@"Cannot remove existing file '%@': %@", 
+                                       fileName, [error localizedDescription]]];
+            [alert addButtonWithTitle:@"OK"];
+            [alert runModal];
+        });
         return nil;
     }
 
     NSString *subdirName = [objectName lastPathComponent];
     NSString *destinationPath = [directoryPath stringByAppendingPathComponent:subdirName];
     if (ifExists && [[NSFileManager defaultManager] fileExistsAtPath:destinationPath]) {
-        NSAlert *alert = [[[NSAlert alloc] init] autorelease];
-        [alert setMessageText:@"File exists"];
-        [alert setInformativeText:[NSString stringWithFormat:@"A file or directory named '%@' already exists, do you want to replace it?", subdirName]];
-        [alert addButtonWithTitle:@"OK"];
-        [alert addButtonWithTitle:@"Cancel"];
-        NSInteger choice = [alert runModal];
+        __block NSInteger choice;
+        dispatch_sync(dispatch_get_main_queue(), ^{
+            NSAlert *alert = [[NSAlert alloc] init];
+            [alert setMessageText:@"File exists"];
+            [alert setInformativeText:[NSString stringWithFormat:@"A file or directory named '%@' already exists, do you want to replace it?", subdirName]];
+            [alert addButtonWithTitle:@"OK"];
+            [alert addButtonWithTitle:@"Cancel"];
+            choice = [alert runModal];
+        });
         if (choice == NSAlertSecondButtonReturn)
             return nil;
     }
     NSError *error = nil;
     [fileManager createDirectoryAtPath:[directoryPath stringByAppendingPathComponent:subdirName] withIntermediateDirectories:YES attributes:nil error:&error];
     if (error) {
-        NSLog(@"Cannot create directory at '%@': %@", directoryPath, error);
-        NSAlert *alert = [[[NSAlert alloc] init] autorelease];
-        [alert setMessageText:@"Create Directory Error"];
-        [alert setInformativeText:[NSString stringWithFormat:@"Cannot create directory at '%@': %@", 
-                                   directoryPath, error]];
-        [alert addButtonWithTitle:@"OK"];
-        [alert runModal];
+        DLog(@"Cannot create directory at '%@': %@", directoryPath, error);
+        dispatch_async(dispatch_get_main_queue(), ^{
+            NSAlert *alert = [[NSAlert alloc] init];
+            [alert setMessageText:@"Create Directory Error"];
+            [alert setInformativeText:[NSString stringWithFormat:@"Cannot create directory at '%@': %@", 
+                                       directoryPath, [error localizedDescription]]];
+            [alert addButtonWithTitle:@"OK"];
+            [alert runModal];
+        });
     }
     
     for (ASIPithosObject *object in objects) {
-        if ([PithosUtilities isContentTypeDirectory:object.contentType]) {
+        if ([self isContentTypeDirectory:object.contentType]) {
             NSString *subdirDirectoryPath = [directoryPath stringByAppendingPathComponent:subdirName];
             subdirDirectoryPath = [subdirDirectoryPath stringByAppendingPathComponent:[object.name substringFromIndex:subdirPrefixLength]];
             
             if (!directoryExists) {
                 [fileManager createDirectoryAtPath:subdirDirectoryPath withIntermediateDirectories:YES attributes:nil error:&error];
                 if (error) {
-                    NSLog(@"Cannot create directory at '%@': %@", subdirDirectoryPath, error);
-                    NSAlert *alert = [[[NSAlert alloc] init] autorelease];
-                    [alert setMessageText:@"Create Directory Error"];
-                    [alert setInformativeText:[NSString stringWithFormat:@"Cannot create directory at '%@': %@", 
-                                               subdirDirectoryPath, error]];
-                    [alert addButtonWithTitle:@"OK"];
-                    [alert runModal];
+                    DLog(@"Cannot create directory at '%@': %@", subdirDirectoryPath, error);
+                    dispatch_async(dispatch_get_main_queue(), ^{
+                        NSAlert *alert = [[NSAlert alloc] init];
+                        [alert setMessageText:@"Create Directory Error"];
+                        [alert setInformativeText:[NSString stringWithFormat:@"Cannot create directory at '%@': %@", 
+                                                   subdirDirectoryPath, [error localizedDescription]]];
+                        [alert addButtonWithTitle:@"OK"];
+                        [alert runModal];
+                    });
                 }
             } else if (!directoryIsDirectory) {
                 [fileManager removeItemAtPath:subdirDirectoryPath error:&error];
                 if (error) {
-                    NSLog(@"Cannot remove existing file at '%@': %@", subdirDirectoryPath, error);
-                    NSAlert *alert = [[[NSAlert alloc] init] autorelease];
-                    [alert setMessageText:@"Remove File Error"];
-                    [alert setInformativeText:[NSString stringWithFormat:@"Cannot remove existing file at '%@': %@", 
-                                               subdirDirectoryPath, error]];
-                    [alert addButtonWithTitle:@"OK"];
-                    [alert runModal];
+                    DLog(@"Cannot remove existing file at '%@': %@", subdirDirectoryPath, error);
+                    dispatch_async(dispatch_get_main_queue(), ^{
+                        NSAlert *alert = [[NSAlert alloc] init];
+                        [alert setMessageText:@"Remove File Error"];
+                        [alert setInformativeText:[NSString stringWithFormat:@"Cannot remove existing file at '%@': %@", 
+                                                   subdirDirectoryPath, [error localizedDescription]]];
+                        [alert addButtonWithTitle:@"OK"];
+                        [alert runModal];
+                    });
                 }
             }
         } else {
     strcpy(tempFileNameCString, tempFileTemplateCString);
     int fileDescriptor = mkstemp(tempFileNameCString);
     NSString *tempFilePath = [fileManager stringWithFileSystemRepresentation:tempFileNameCString length:strlen(tempFileNameCString)];
+    free(tempFileNameCString);
     if (fileDescriptor == -1) {
-        NSAlert *alert = [[[NSAlert alloc] init] autorelease];
-        [alert setMessageText:@"Create Temporary File Error"];
-        [alert setInformativeText:[NSString stringWithFormat:@"Cannot create temporary file at '%@'", tempFilePath]];
-        [alert addButtonWithTitle:@"OK"];
-        [alert runModal];
+        dispatch_async(dispatch_get_main_queue(), ^{
+            NSAlert *alert = [[NSAlert alloc] init];
+            [alert setMessageText:@"Create Temporary File Error"];
+            [alert setInformativeText:[NSString stringWithFormat:@"Cannot create temporary file at '%@'", tempFilePath]];
+            [alert addButtonWithTitle:@"OK"];
+            [alert runModal];
+        });
         return nil;
     }
-    free(tempFileNameCString);
     NSFileHandle *tempFileHandle = [[NSFileHandle alloc] initWithFileDescriptor:fileDescriptor closeOnDealloc:YES];
 
     [missingBlocks enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop){
     NSError *error = nil;
     NSArray *subPaths = [fileManager subpathsOfDirectoryAtPath:directoryPath error:&error];
     if (error) {
-        NSAlert *alert = [[[NSAlert alloc] init] autorelease];
-        [alert setMessageText:@"Directory Read Error"];
-        [alert setInformativeText:[NSString stringWithFormat:@"Cannot read contents of directory '%@': %@", 
-                                   [directoryPath lastPathComponent], error]];
-        [alert addButtonWithTitle:@"OK"];
-        [alert runModal];
+        dispatch_async(dispatch_get_main_queue(), ^{
+            NSAlert *alert = [[NSAlert alloc] init];
+            [alert setMessageText:@"Directory Read Error"];
+            [alert setInformativeText:[NSString stringWithFormat:@"Cannot read contents of directory '%@': %@", 
+                                       [directoryPath lastPathComponent], [error localizedDescription]]];
+            [alert addButtonWithTitle:@"OK"];
+            [alert runModal];
+        });
         return nil;
     }
     
             if (!isDirectory) {
                 hashes = [HashMapHash objectHashMapStrings:filePath withBlockHash:blockHash andBlockSize:blockSize];
                 if (hashes) {
-                    subObjectName = [objectName stringByAppendingPathComponent:objectNameSuffix];
+                    subObjectName = [[objectName stringByAppendingPathComponent:objectNameSuffix] precomposedStringWithCanonicalMapping];
                     fileName = [filePath lastPathComponent];
                     if ([filePath hasSuffix:@"/"])
                         fileName = [fileName stringByAppendingString:@"/"];
                     contentType = [self contentTypeOfFile:filePath error:&error];
                     if (contentType == nil)
                         contentType = @"application/octet-stream";
+                    #if DEBUG_PITHOS
                     if (error)
-                        NSLog(@"contentType detection error: %@", error);
+                        DLog(@"contentType detection error: %@", error);
+                    #endif
                     objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithPithos:pithos 
                                                                                containerName:containerName 
                                                                                   objectName:subObjectName 
                 }
                 
             } else {
-                subObjectName = [objectName stringByAppendingPathComponent:objectNameSuffix];
+                subObjectName = [[objectName stringByAppendingPathComponent:objectNameSuffix] precomposedStringWithCanonicalMapping];
                 fileName = [filePath lastPathComponent];
                 objectRequest = [ASIPithosObjectRequest writeObjectDataRequestWithPithos:pithos 
                                                                            containerName:containerName 
 #pragma mark -
 #pragma mark Copy
 
-+ (ASIPithosObjectRequest *)copyObjectRequestWithPithos:(ASIPithos *)pithos 
++ (ASIPithosObjectRequest *)cpyObjectRequestWithPithos:(ASIPithos *)pithos 
                                           containerName:(NSString *)containerName 
                                              objectName:(NSString *)objectName 
                                destinationContainerName:(NSString *)destinationContainerName 
                                           sharingAccount:nil])
         return nil;
     
-    ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithPithos:pithos 
+    ASIPithosObjectRequest *objectRequest = [ASIPithosObjectRequest cpyObjectDataRequestWithPithos:pithos 
                                                                                       containerName:containerName 
                                                                                          objectName:objectName 
                                                                                         contentType:nil 
     return objectRequest;
 }
 
-+ (NSArray *)copyObjectRequestsForSubdirWithPithos:(ASIPithos *)pithos 
++ (NSArray *)cpyObjectRequestsForSubdirWithPithos:(ASIPithos *)pithos 
                                      containerName:(NSString *)containerName 
                                         objectName:(NSString *)objectName 
                           destinationContainerName:(NSString *)destinationContainerName 
     ASIPithosObjectRequest *objectRequest;
     if ([objectName isEqualToString:destinationObjectName]) {
         if (![objectName hasSuffix:@"/"]) {
-            objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithPithos:pithos 
+            objectRequest = [ASIPithosObjectRequest cpyObjectDataRequestWithPithos:pithos 
                                                                       containerName:containerName 
                                                                          objectName:objectName 
                                                                         contentType:nil 
             [objectRequests addObject:objectRequest];
         }
         for (ASIPithosObject *object in objects) {
-            objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithPithos:pithos 
+            objectRequest = [ASIPithosObjectRequest cpyObjectDataRequestWithPithos:pithos 
                                                                       containerName:containerName 
                                                                          objectName:object.name 
                                                                         contentType:nil 
         }
     } else {
         if (![objectName hasSuffix:@"/"]) {
-            objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithPithos:pithos 
+            objectRequest = [ASIPithosObjectRequest cpyObjectDataRequestWithPithos:pithos 
                                                                       containerName:containerName 
                                                                          objectName:objectName 
                                                                         contentType:nil 
                                                                    withString:destinationObjectName
                                                                       options:NSAnchoredSearch
                                                                         range:prefixRange];
-            objectRequest = [ASIPithosObjectRequest copyObjectDataRequestWithPithos:pithos 
+            objectRequest = [ASIPithosObjectRequest cpyObjectDataRequestWithPithos:pithos 
                                                                       containerName:containerName 
                                                                          objectName:object.name 
                                                                         contentType:nil 
 + (NSUInteger)bytesOfFile:(NSString *)filePath {
     NSFileManager *fileManager = [NSFileManager defaultManager];
     NSDictionary *attributes = [fileManager attributesOfItemAtPath:filePath error:nil];
-    return [[attributes objectForKey:NSFileSize] intValue];
+    return [[attributes objectForKey:NSFileSize] unsignedIntegerValue];
 }
 
 // Content type of the file or nil if it cannot be determined
 + (NSString *)contentTypeOfFile:(NSString *)filePath error:(NSError **)error {
-    NSURLResponse *response = nil;
-    [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:filePath] 
-                                                             cachePolicy:NSURLCacheStorageNotAllowed 
-                                                         timeoutInterval:.1] 
-                          returningResponse:&response 
-                                      error:error];
-    return [response MIMEType];
+    // Based on http://www.ddeville.me/2011/12/mime-to-UTI-cocoa/
+    // and Apple example ImageBrowserViewAppearance/ImageBrowserController.m
+    LSItemInfoRecord info;
+    CFStringRef uti = NULL;
+    CFURLRef url = CFURLCreateWithFileSystemPath(NULL, (CFStringRef)filePath, kCFURLPOSIXPathStyle, FALSE);
+    if (LSCopyItemInfoForURL(url, kLSRequestExtension | kLSRequestTypeCreator, &info) == noErr) {
+        // Obtain the UTI using the file information.
+        // If there is a file extension, get the UTI.
+        if (info.extension != NULL) {
+            uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, info.extension, kUTTypeData);
+            CFRelease(info.extension);
+        }
+        // No UTI yet
+        if (uti == NULL) {
+            // If there is an OSType, get the UTI.
+            CFStringRef typeString = UTCreateStringForOSType(info.filetype);
+            if ( typeString != NULL) {
+                uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassOSType, typeString, kUTTypeData);
+                CFRelease(typeString);
+            }
+        }
+        if (uti != NULL) {
+            CFStringRef MIMEType = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType);
+            CFRelease(uti);
+            CFRelease(url);
+            return (__bridge_transfer NSString *)MIMEType;
+        }
+    }
+    CFRelease(url);
+    return nil;
 }
 
 // Creates a directory if it doesn't exist and returns if successful
     return YES;
 }
 
+// Removes contents of a directory and the directory itself if selected
++ (void)removeContentsAtPath:(NSString *)dirPath andDirectory:(BOOL)removeDirectory {
+    NSFileManager *fileManager = [NSFileManager defaultManager];
+    NSError *error = nil;
+    BOOL isDirectory;
+    if (![fileManager fileExistsAtPath:dirPath isDirectory:&isDirectory])
+        return;
+    if (isDirectory) {
+        for (NSString *subPath in [fileManager contentsOfDirectoryAtPath:dirPath error:&error]) {
+            if (error) {
+                [self fileActionFailedAlertWithTitle:@"Directory Contents Error" 
+                                             message:[NSString stringWithFormat:@"Cannot get contents of directory at '%@'", dirPath] 
+                                               error:error];
+                break;
+            }
+            NSString *subFilePath = [dirPath stringByAppendingPathComponent:subPath];
+            if (![fileManager removeItemAtPath:subFilePath error:&error] || error) {
+                [self fileActionFailedAlertWithTitle:@"Remove File Error" 
+                                             message:[NSString stringWithFormat:@"Cannot remove file at '%@'", subFilePath] 
+                                               error:error];
+            }
+            error = nil;
+        }
+        if (removeDirectory && (![fileManager removeItemAtPath:dirPath error:&error] || error)) {
+            [self fileActionFailedAlertWithTitle:@"Remove Directory Error"
+                                         message:[NSString stringWithFormat:@"Cannot remove directory at '%@'", dirPath]
+                                           error:error];
+        }
+    } else if (![fileManager removeItemAtPath:dirPath error:&error] || error) {
+        [self fileActionFailedAlertWithTitle:@"Remove File Error" 
+                                     message:[NSString stringWithFormat:@"Cannot remove file at '%@'", dirPath] 
+                                       error:error];
+    }
+}
+
+// Removes contents of a directory
++ (void)removeContentsAtPath:(NSString *)dirPath {
+    [self removeContentsAtPath:dirPath andDirectory:NO];
+}
+
 // Returns if an object is a directory based on its content type
 + (BOOL)isContentTypeDirectory:(NSString *)contentType {
     return ([contentType isEqualToString:@"application/directory"] ||
                                                                                          objectName:objectName];
     if (sharingAccount)
         [objectRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
-    ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
-    [networkQueue go];
-    [networkQueue addOperations:[NSArray arrayWithObject:[PithosUtilities prepareRequest:objectRequest]] waitUntilFinished:YES];
-    *error = [objectRequest error];
-    if (*error) {
-        [self httpRequestErrorAlertWithRequest:objectRequest];
-        return NO;
-    } else if (objectRequest.responseStatusCode == 200) {
-        *isDirectory = [self isContentTypeDirectory:[objectRequest contentType]];
-        return YES;
+    [self startAndWaitForRequest:objectRequest];
+    if (error != NULL) {
+        *error = [objectRequest error];
+        if (*error) {
+            [self httpRequestErrorAlertWithRequest:objectRequest];
+            return NO;
+        } else if (objectRequest.responseStatusCode == 200) {
+            *isDirectory = [self isContentTypeDirectory:[objectRequest contentType]];
+            return YES;
+        }
     }
     return NO;
 }
     if (error) {
         return NO;
     } else if (objectExists) {
-        NSAlert *alert = [[[NSAlert alloc] init] autorelease];
-        if (isDirectory) {
-            [alert setMessageText:@"Directory Exists"];
-            if (sharingAccount)
-                [alert setInformativeText:[NSString stringWithFormat:@"A directory with path '%@' in the container '%@' of user '%@' already exists, do you want to replace it?", objectName, containerName, sharingAccount]];
-            else
-                [alert setInformativeText:[NSString stringWithFormat:@"A directory with path '%@' in the container '%@' already exists, do you want to replace it?", objectName, containerName]];
-        } else {
-            [alert setMessageText:@"Object Exists"];
-            if (sharingAccount)
-                [alert setInformativeText:[NSString stringWithFormat:@"An object with path '%@' in the container '%@' of user '%@' already exists, do you want to replace it?", objectName, containerName, sharingAccount]];
-            else
-                [alert setInformativeText:[NSString stringWithFormat:@"An object with path '%@' in the container '%@' already exists, do you want to replace it?", objectName, containerName]];
-        }
-        [alert addButtonWithTitle:@"OK"];
-        [alert addButtonWithTitle:@"Cancel"];
-        NSInteger choice = [alert runModal];
+        __block NSInteger choice;
+        dispatch_sync(dispatch_get_main_queue(), ^{
+            NSAlert *alert = [[NSAlert alloc] init];
+            if (isDirectory) {
+                [alert setMessageText:@"Directory Exists"];
+                if (sharingAccount)
+                    [alert setInformativeText:[NSString stringWithFormat:@"A directory with path '%@' in the container '%@' of user '%@' already exists, do you want to replace it?", objectName, containerName, sharingAccount]];
+                else
+                    [alert setInformativeText:[NSString stringWithFormat:@"A directory with path '%@' in the container '%@' already exists, do you want to replace it?", objectName, containerName]];
+            } else {
+                [alert setMessageText:@"Object Exists"];
+                if (sharingAccount)
+                    [alert setInformativeText:[NSString stringWithFormat:@"An object with path '%@' in the container '%@' of user '%@' already exists, do you want to replace it?", objectName, containerName, sharingAccount]];
+                else
+                    [alert setInformativeText:[NSString stringWithFormat:@"An object with path '%@' in the container '%@' already exists, do you want to replace it?", objectName, containerName]];
+            }
+            [alert addButtonWithTitle:@"OK"];
+            [alert addButtonWithTitle:@"Cancel"];
+            choice = [alert runModal];
+        });
         if (choice == NSAlertSecondButtonReturn)
             return NO;
     }
                                                                                                         until:nil];
         if (sharingAccount)
             [containerRequest setRequestUserFromDefaultTo:sharingAccount withPithos:pithos];
-        ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
-        [networkQueue go];
-        [networkQueue addOperations:[NSArray arrayWithObject:[PithosUtilities prepareRequest:containerRequest]] waitUntilFinished:YES];
+        [self startAndWaitForRequest:containerRequest];
         if ([containerRequest error]) {
             [self httpRequestErrorAlertWithRequest:containerRequest];
             return nil;
         objectNameExtraSuffix = [objectName substringFromIndex:lastDotRange.location];
     } else if ([objectName hasSuffix:@"/"]) {
         objectNamePrefix = [objectName substringToIndex:([objectName length] - 1)];
-        objectNameExtraSuffix = [NSString stringWithString:@"/"];
+        objectNameExtraSuffix = @"/";
     } else {
         objectNamePrefix = [NSString stringWithString:objectName];
         objectNameExtraSuffix = [NSString string];
     NSString *subdirNameExtraSuffix;
     if ([subdirName hasSuffix:@"/"]) {
         subdirNamePrefix = [subdirName substringToIndex:([subdirName length] - 1)];
-        subdirNameExtraSuffix = [NSString stringWithString:@"/"];
+        subdirNameExtraSuffix = @"/";
     } else {
         subdirNamePrefix = [NSString stringWithString:subdirName];
         subdirNameExtraSuffix = [NSString string];
 #pragma mark -
 #pragma mark Alerts
 
-+ (NSInteger)httpRequestErrorAlertWithRequest:(ASIPithosRequest *)request {
++ (void)httpRequestErrorAlertWithRequest:(ASIPithosRequest *)request {
+    if (request.responseStatusCode == 401) {
+        [self httpAuthenticationError];
+        return;
+    }
     NSString *message = [NSString stringWithFormat:
                          @"HTTP request error: %@\n%@ URL: %@\nRequest Headers: %@\nResponse Headers: %@\nResponse String: %@", 
-                         [request error], 
+                         [[request error] localizedDescription], 
                          request.requestMethod, 
                          request.url, 
                          [request requestHeaders], 
                          [request responseHeaders], 
                          [request responseString]];
-    NSLog(@"%@", message);
-    NSAlert *alert = [[[NSAlert alloc] init] autorelease];
-    [alert setMessageText:@"HTTP Request Error"];
-    [alert setInformativeText:message];
-    [alert addButtonWithTitle:@"OK"];
-    return [alert runModal];
+    DLog(@"%@", message);
+    dispatch_async(dispatch_get_main_queue(), ^{    
+        @autoreleasepool {
+            NSAlert *alert = [[NSAlert alloc] init];
+            [alert setMessageText:@"HTTP Request Error"];
+            [alert setInformativeText:message];
+            [alert addButtonWithTitle:@"OK"];
+            [alert runModal];
+        }
+    });
 }
 
-+ (NSInteger)unexpectedResponseStatusAlertWithRequest:(ASIPithosRequest *)request {
++ (void)unexpectedResponseStatusAlertWithRequest:(ASIPithosRequest *)request {
+    if (request.responseStatusCode == 401) {
+        [self httpAuthenticationError];
+        return;
+    }
     NSString *message = [NSString stringWithFormat:
                          @"Unexpected response status %d: %@\n%@ URL: %@\nRequest Headers: %@\nResponse Headers: %@\nResponse String: %@", 
                          request.responseStatusCode, 
                          [request requestHeaders], 
                          [request responseHeaders], 
                          [request responseString]];
-    NSLog(@"%@", message);
-    NSAlert *alert = [[[NSAlert alloc] init] autorelease];
-    [alert setMessageText:@"Unexpected Response Status"];
-    [alert setInformativeText:message];
-    [alert addButtonWithTitle:@"OK"];
-    return [alert runModal];
+    DLog(@"%@", message);
+    dispatch_async(dispatch_get_main_queue(), ^{
+        @autoreleasepool {
+            NSAlert *alert = [[NSAlert alloc] init];
+            [alert setMessageText:@"Unexpected Response Status"];
+            [alert setInformativeText:message];
+            [alert addButtonWithTitle:@"OK"];
+            [alert runModal];
+        }
+    });
+}
+
++ (void)httpAuthenticationError {
+    dispatch_async(dispatch_get_main_queue(), ^{
+        @autoreleasepool {
+            NSAlert *alert = [[NSAlert alloc] init];
+            [alert setMessageText:@"Authentication Error"];
+            [alert setInformativeText:@"Authentication error, please check your token or login again"];
+            [alert addButtonWithTitle:@"OK"];
+            [alert runModal];
+        }
+    });
 }
 
-+ (NSInteger)fileActionFailedAlertWithTitle:(NSString *)title message:(NSString *)message error:(NSError *)error {
-    NSAlert *alert = [[[NSAlert alloc] init] autorelease];
-    [alert setMessageText:title];
-    if (error)
-        [alert setInformativeText:[NSString stringWithFormat:@"%@: %@", message, error]];
-    else
-        [alert setInformativeText:message];
-    [alert addButtonWithTitle:@"OK"];
-    return [alert runModal];
++ (void)fileActionFailedAlertWithTitle:(NSString *)title message:(NSString *)message error:(NSError *)error {
+    dispatch_async(dispatch_get_main_queue(), ^{
+        @autoreleasepool {
+            NSAlert *alert = [[NSAlert alloc] init];
+            [alert setMessageText:title];
+            if (error)
+                [alert setInformativeText:[NSString stringWithFormat:@"%@: %@", message, [error localizedDescription]]];
+            else
+                [alert setInformativeText:message];
+            [alert addButtonWithTitle:@"OK"];
+            [alert runModal];
+        }
+    });
 }
 
 #pragma mark -
 }
 
 + (ASIPithosRequest *)copyRequest:(ASIPithosRequest *)request {
-    NSMutableDictionary *userInfo = (NSMutableDictionary *)[[request.userInfo retain] autorelease];
+    NSMutableDictionary *userInfo = (NSMutableDictionary *)request.userInfo;
     request.userInfo = nil;
     ASIPithosRequest *newRequest = [request copy];
     newRequest.userInfo = userInfo;
     return newRequest;
 }
 
++ (void)startAndWaitForRequest:(ASIPithosRequest *)request {
+    ASINetworkQueue *networkQueue = [ASINetworkQueue queue];
+    [networkQueue go];
+    [networkQueue addOperations:[NSArray arrayWithObject:[self prepareRequest:request]] waitUntilFinished:YES];
+}
+
 @end