Improve UUID translation in nodes
[pithos-macos] / pithos-macos / pithos_macosAppDelegate.m
index 76944c3..95d22ae 100644 (file)
@@ -2,7 +2,7 @@
 //  pithos_macosAppDelegate.m
 //  pithos-macos
 //
-// Copyright 2011 GRNET S.A. All rights reserved.
+// Copyright 2011-2012 GRNET S.A. All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or
 // without modification, are permitted provided that the following
 // or implied, of GRNET S.A.
 
 #import "pithos_macosAppDelegate.h"
+#import "PithosAccount.h"
 #import "PithosBrowserController.h"
 #import "PithosPreferencesController.h"
+#import "PithosSyncDaemon.h"
 #import "ASIPithosRequest.h"
-#import "ASIDownloadCache.h"
+#import "ASIPithos.h"
+#import "LastCompletedSyncTransformer.h"
 
 @implementation pithos_macosAppDelegate
-@synthesize storageURLPrefix, publicURLPrefix, loginURLPrefix, aboutURL;
+@synthesize pithosBrowserController, pithosPreferencesController, alwaysNo, openAtLoginEnabled, openAtLogin, activated, 
+currentPithosAccount, pithosAccounts, pithosAccountsDictionary, syncPithosAccount, activityFacilityTimeInterval, checkForUpdatesNotRunning;
 
 - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
-    NSURL *testURL;
-    storageURLPrefix = [[NSUserDefaults standardUserDefaults] stringForKey:@"PithosStorageURLPrefix"];
-    if (!storageURLPrefix) {
-        storageURLPrefix = [NSString stringWithString:@"https://pithos.dev.grnet.gr/v1"];
-    } else {
-        testURL = [NSURL URLWithString:storageURLPrefix];
-        if (!testURL || !testURL.scheme || !testURL.host)
-            storageURLPrefix = [NSString stringWithString:@"https://pithos.dev.grnet.gr/v1"];
-    }
-    [storageURLPrefix retain];
+    [[NSAppleEventManager sharedAppleEventManager] setEventHandler:self 
+                                                       andSelector:@selector(handleAppleEvent:withReplyEvent:) 
+                                                     forEventClass:kInternetEventClass 
+                                                        andEventID:kAEGetURL];
     
-    publicURLPrefix = [[NSUserDefaults standardUserDefaults] stringForKey:@"PithosPublicURLPrefix"];
-    if (!publicURLPrefix) {
-        publicURLPrefix = [NSString stringWithString:@"https://pithos.dev.grnet.gr"];
-    } else {
-        testURL = [NSURL URLWithString:publicURLPrefix];
-        if (!testURL || !testURL.scheme || !testURL.host)
-            publicURLPrefix = [NSString stringWithString:@"https://pithos.dev.grnet.gr"];
+    // Based on: https://github.com/Mozketo/LaunchAtLoginController
+    // and: http://cocoatutorial.grapewave.com/2010/02/creating-andor-removing-a-login-item/
+    loginItems = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
+    if (loginItems) {
+        LSSharedFileListAddObserver(loginItems, CFRunLoopGetMain(), (CFStringRef)NSDefaultRunLoopMode, LSSharedFileListChanged, (__bridge void *)(self));
+        LSSharedFileListChanged(loginItems, (__bridge void *)(self));
+        self.openAtLoginEnabled = YES;
     }
-    [publicURLPrefix retain];
     
-    loginURLPrefix = [[NSUserDefaults standardUserDefaults] stringForKey:@"PithosLoginURLPrefix"];
-    if (!loginURLPrefix) {
-        loginURLPrefix = [NSString stringWithString:@"https://pithos.dev.grnet.gr/login"];
-    } else {
-        testURL = [NSURL URLWithString:loginURLPrefix];
-        if (!testURL || !testURL.scheme || !testURL.host)
-            loginURLPrefix = [NSString stringWithString:@"https://pithos.dev.grnet.gr/login"];
+    userDefaults = [NSUserDefaults standardUserDefaults];
+    
+    syncTimeInterval = [userDefaults doubleForKey:@"syncTimeInterval"];
+    if (syncTimeInterval <= 0.0) {
+        syncTimeInterval = 180.0;
+        [userDefaults setDouble:syncTimeInterval forKey:@"syncTimeInterval"];
+        [userDefaults synchronize];
+    }
+
+    activityFacilityTimeInterval = [userDefaults doubleForKey:@"activityFacilityTimeInterval"];
+    if (activityFacilityTimeInterval <= 0.0) {
+        activityFacilityTimeInterval = 0.05;
+        [userDefaults setDouble:activityFacilityTimeInterval forKey:@"activityFacilityTimeInterval"];
+        [userDefaults synchronize];
     }
-    [loginURLPrefix retain];
 
-    aboutURL = [[NSUserDefaults standardUserDefaults] stringForKey:@"PithosAboutURL"];
-    if (!aboutURL) {
-        aboutURL = [NSString stringWithString:@"https://pithos.dev.grnet.gr/docs"];
+    NSData *tmpData = [userDefaults objectForKey:@"pithosAccounts"];
+    NSArray *tmpArray;
+    if (tmpData && (tmpArray = [NSKeyedUnarchiver unarchiveObjectWithData:tmpData]))
+        self.pithosAccounts = [NSMutableArray arrayWithArray:tmpArray];
+    else
+        self.pithosAccounts = [NSMutableArray array];
+    
+    if (![pithosAccounts count]) {
+        [pithosAccounts addObject:[PithosAccount pithosAccount]];
+        self.pithosAccounts = self.pithosAccounts;
     } else {
-        testURL = [NSURL URLWithString:aboutURL];
-        if (!testURL || !testURL.scheme || !testURL.host)
-            aboutURL = [NSString stringWithString:@"https://pithos.dev.grnet.gr/docs"];
+        self.activated = YES;
     }
-    [aboutURL retain];
     
-    [[NSAppleEventManager sharedAppleEventManager] setEventHandler:self 
-                                                       andSelector:@selector(handleAppleEvent:withReplyEvent:) 
-                                                     forEventClass:kInternetEventClass 
-                                                        andEventID:kAEGetURL];
-    [self showPithosBrowser:self];
+    pithosAccountsDictionary = [[NSMutableDictionary alloc] initWithCapacity:[pithosAccounts count]];
+    for (PithosAccount *pithosAccount in pithosAccounts) {
+        [pithosAccountsDictionary setObject:pithosAccount forKey:pithosAccount.name];
+        if (!currentPithosAccount && pithosAccount.active)
+            self.currentPithosAccount = pithosAccount;
+    }
+    if (!currentPithosAccount)
+        self.currentPithosAccount = [pithosAccounts objectAtIndex:0];
+    self.pithosBrowserController.pithosAccountManager = currentPithosAccount;
     
-    [self authenticateWithAuthUser:[[userDefaultsController values] valueForKey:@"authUser"] 
-                         authToken:[[userDefaultsController values] valueForKey:@"authToken"]];
+    if (currentPithosAccount.active) {
+        [self savePithosAccounts:self];
+        [self showPithosBrowser:self];
+        self.pithosBrowserController.pithos = currentPithosAccount.pithos;
+    } else {
+        // XXX maybe call specifically to go to new account tab
+        [self showPithosPreferences:self];
+    }
+
+    syncTimer = [NSTimer scheduledTimerWithTimeInterval:syncTimeInterval 
+                                                  target:self 
+                                                selector:@selector(sync) 
+                                                userInfo:nil 
+                                                 repeats:YES];
+    [syncTimer fire];
+    
+    @synchronized(self) {
+        self.checkForUpdatesNotRunning = YES;
+    }
+    [self checkForUpdates];
 }
 
 // Based on: http://cocoatutorial.grapewave.com/2010/01/creating-a-status-bar-application/
 // and: http://www.cocoadev.com/index.pl?ThumbnailImages
 - (void)awakeFromNib {
-    NSImage *sourceImage = [NSImage imageNamed:NSImageNameFolder];
+    NSImage *sourceImage = [NSImage imageNamed:@"pithos-large.png"];
     
-    NSImage *smallImage = [[[NSImage alloc] initWithSize:NSMakeSize(18, 18)] autorelease];
+    NSImage *smallImage = [[NSImage alloc] initWithSize:NSMakeSize(18, 18)];
     [smallImage lockFocus];
     [[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh];
     [sourceImage setSize:NSMakeSize(18, 18)];
-    [sourceImage compositeToPoint:NSZeroPoint operation:NSCompositeCopy];
+    [sourceImage drawAtPoint:NSZeroPoint fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0];
     [smallImage unlockFocus];
     
-    statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength] retain];
+    statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
     [statusItem setMenu:statusMenu];
     [statusItem setImage:sourceImage];
     [statusItem setHighlightMode:YES];
+    
+    self.alwaysNo = NO;
 }
 
 - (void)handleAppleEvent:(NSAppleEventDescriptor *)event withReplyEvent: (NSAppleEventDescriptor *)replyEvent {
     NSURL *url = [NSURL URLWithString:[[event paramDescriptorForKeyword:keyDirectObject] stringValue]];
     NSString *host = [url host];
        NSString *query = [url query];
-    NSLog(@"host : '%@', query: '%@'", host, query);
+    PithosAccount *pithosAccount = [pithosAccountsDictionary objectForKey:[ASIPithosRequest decodeFromPercentEscape:[url lastPathComponent]]];
     NSProcessInfo *processInfo = [NSProcessInfo processInfo];
-    if ([host isEqualToString:[NSString stringWithFormat:@"%@_%d", [processInfo processName], [processInfo processIdentifier]]] && query) {
+    if ([host isEqualToString:[NSString stringWithFormat:@"%d", [processInfo processIdentifier]]] && pithosAccount && query) {
         // user=
         NSString *authUser;
         NSRange userRange = [query rangeOfString:@"user=" options:NSCaseInsensitiveSearch];
                          stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
         }
         
-        NSLog(@"query authUser: '%@', authToken: '%@'", authUser, authToken);
+        DLog(@"query authUser: '%@', authToken: '%@'", authUser, authToken);
         if ([authUser length] && [authToken length]) {
-            [[userDefaultsController values] setValue:authUser forKey:@"authUser"];
-            [[userDefaultsController values] setValue:authToken forKey:@"authToken"];
-            
-            [self authenticateWithAuthUser:authUser authToken:authToken];
+            [pithosAccount authenticateWithServerURL:nil authUser:authUser authToken:authToken];
+            [self savePithosAccounts:self];
+            if (self.pithosPreferencesController && [self.pithosPreferencesController.selectedPithosAccount isEqualTo:pithosAccount]) {
+                self.pithosPreferencesController.authUser = pithosAccount.authUser;
+                self.pithosPreferencesController.authToken = pithosAccount.authToken;
+            }
+            self.activated = YES;
+            if ([pithosAccount isEqualTo:currentPithosAccount]) {
+                [self showPithosBrowser:self];
+                self.pithosBrowserController.pithos = pithosAccount.pithos;
+            }
         }
         // XXX else maybe show an error message?
     }
     // XXX else maybe show an error message?
 }
 
+- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
+    [self savePithosAccounts:self];
+    if ([self.pithosBrowserController operationsPending]) {
+        NSAlert *alert = [[NSAlert alloc] init];
+        [alert setMessageText:@"Pending Operations"];
+        [alert setInformativeText:@"There are pending operations in the browser, do you want to quit and cancel them?"];
+        [alert addButtonWithTitle:@"OK"];
+        [alert addButtonWithTitle:@"Cancel"];
+        NSInteger choice = [alert runModal];
+        if (choice == NSAlertSecondButtonReturn) 
+            return NSTerminateCancel;
+    }
+    if (loginItems) {
+        LSSharedFileListRemoveObserver(loginItems, CFRunLoopGetMain(), (CFStringRef)NSDefaultRunLoopMode, LSSharedFileListChanged, (__bridge void *)(self));
+        CFRelease(loginItems);
+    }
+    return NSTerminateNow;
+}
+
+#pragma mark -
+#pragma mark Callbacks
+
+- (void)loginItemsChanged {
+    NSURL *appURL = [[NSBundle mainBundle] bundleURL];
+    LSSharedFileListItemRef appItem = NULL;
+    NSArray *snapshot = (__bridge_transfer NSArray *)(LSSharedFileListCopySnapshot(loginItems, NULL));
+    for (id itemObject in snapshot) {
+        LSSharedFileListItemRef item = (__bridge LSSharedFileListItemRef)itemObject;
+        UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes;
+        CFURLRef currentItemURL = NULL;
+        LSSharedFileListItemResolve(item, resolutionFlags, &currentItemURL, NULL);
+        if (currentItemURL && CFEqual(currentItemURL, (__bridge CFTypeRef)(appURL))) {
+            CFRelease(currentItemURL);
+            appItem = item;
+            break;
+        }
+        if (currentItemURL)
+            CFRelease(currentItemURL);
+    }
+    
+    if (appItem && (!openAtLogin || !openAtLoginEnabled))
+        self.openAtLogin = YES;
+    else if (!appItem && (openAtLogin || !openAtLoginEnabled))
+        self.openAtLogin = NO;
+}
+
+void LSSharedFileListChanged(LSSharedFileListRef inList, void *context) {
+    pithos_macosAppDelegate *self = (__bridge id)context;
+    [self loginItemsChanged];
+}
+
+#pragma mark -
+#pragma mark Properties
+
+- (PithosBrowserController *)pithosBrowserController {
+    if (!pithosBrowserController) {
+        pithosBrowserController = [[PithosBrowserController alloc] init];
+    }
+    return pithosBrowserController;
+}
+
+- (PithosPreferencesController *)pithosPreferencesController {
+    if (!pithosPreferencesController) {
+        pithosPreferencesController = [[PithosPreferencesController alloc] init];
+    }
+    return pithosPreferencesController;
+}
+
+- (void)setOpenAtLogin:(BOOL)anOpenAtLogin {
+    if (!openAtLoginEnabled) {
+        openAtLogin = anOpenAtLogin;
+    } else if (anOpenAtLogin != openAtLogin) {
+        NSURL *appURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
+        LSSharedFileListItemRef appItem = NULL;
+        NSArray *snapshot = (__bridge_transfer NSArray *)(LSSharedFileListCopySnapshot(loginItems, NULL));
+        for (id itemObject in snapshot) {
+            LSSharedFileListItemRef item = (__bridge LSSharedFileListItemRef)itemObject;
+            UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes;
+            CFURLRef currentItemURL = NULL;
+            LSSharedFileListItemResolve(item, resolutionFlags, &currentItemURL, NULL);
+            if (currentItemURL && CFEqual(currentItemURL, (__bridge CFTypeRef)(appURL))) {
+                CFRelease(currentItemURL);
+                appItem = item;
+                break;
+            }
+            if (currentItemURL)
+                CFRelease(currentItemURL);
+        }
+        
+        if (anOpenAtLogin) {
+            if (!appItem)
+                LSSharedFileListInsertItemURL(loginItems, kLSSharedFileListItemBeforeFirst, NULL, NULL, (__bridge CFURLRef)appURL, NULL, NULL);
+            openAtLogin = YES;
+        } else {
+            if (appItem)
+                LSSharedFileListItemRemove(loginItems, appItem);
+            openAtLogin = NO;
+        }
+    }
+}
+
 #pragma mark -
-#pragma Actions
+#pragma mark NSMenuDelegate
+
+- (void)menuNeedsUpdate:(NSMenu *)menu {
+    NSMenuItem *menuItem;
+    [menu removeAllItems];
+    if ([menu isEqualTo:accountsMenu]) {
+        [menu setAutoenablesItems:NO];
+        for (PithosAccount *pithosAccount in pithosAccounts) {
+            menuItem = [[NSMenuItem alloc] initWithTitle:pithosAccount.name 
+                                                   action:@selector(menuChangePithosAccount:) 
+                                            keyEquivalent:@""];
+            [menuItem setRepresentedObject:pithosAccount];
+            [menuItem setEnabled:pithosAccount.active];
+            [menuItem setState:((pithosAccount.active && [currentPithosAccount isEqualTo:pithosAccount]) ? NSOnState : NSOffState)];
+            [menu addItem:menuItem];
+        }
+    } else if ([menu isEqualTo:lastSyncMenu]) {
+        NSString *menuItemTitle;
+        [menu setAutoenablesItems:NO];
+        for (PithosAccount *pithosAccount in pithosAccounts) {
+            menuItemTitle = [NSString stringWithFormat:@"%@: %@", 
+                             pithosAccount.name, 
+                             [[[LastCompletedSyncTransformer alloc] init] transformedValue:pithosAccount.syncLastCompleted]];
+            if ([pithosAccount isEqualTo:syncPithosAccount] && [pithosAccount.syncDaemon isSyncing])
+                menuItemTitle = [menuItemTitle stringByAppendingString:@" (syncing)"];
+//            menuItem = [[[NSMenuItem alloc] initWithTitle:menuItemTitle 
+//                                                   action:@selector(menuChangeSyncActive:) 
+//                                            keyEquivalent:@""] autorelease];
+//            [menuItem setRepresentedObject:pithosAccount];
+//            [menuItem setEnabled:pithosAccount.active];
+//            [menuItem setState:((pithosAccount.active && pithosAccount.syncActive) ? NSOnState : NSOffState)];
+            menuItem = [[NSMenuItem alloc] initWithTitle:menuItemTitle action:nil keyEquivalent:@""];
+            [menuItem setEnabled:NO];
+            [menuItem setState:NO];
+            [menu addItem:menuItem];
+        }
+        [menu addItem:[NSMenuItem separatorItem]];
+        [menu addItem:[[NSMenuItem alloc] initWithTitle:@"Next Sync" 
+                                                  action:@selector(sync) 
+                                           keyEquivalent:@""]];
+    }
+}
+
+#pragma mark -
+#pragma mark Actions
 
 - (IBAction)showPithosBrowser:(id)sender {
-    [pithosBrowserController showWindow:sender];
-    [[pithosBrowserController window] makeKeyAndOrderFront:sender];
+    if (!activated)
+        return;
+    [self.pithosBrowserController showWindow:sender];
+    [[self.pithosBrowserController window] makeKeyAndOrderFront:sender];
     [NSApp activateIgnoringOtherApps:YES];
 }
 
 - (IBAction)showPithosPreferences:(id)sender {
-    [pithosPreferencesController showWindow:sender];
-    [[pithosPreferencesController window] makeKeyAndOrderFront:sender];
+    [self.pithosPreferencesController showWindow:sender];
+    [[self.pithosPreferencesController window] makeKeyAndOrderFront:sender];
     [NSApp activateIgnoringOtherApps:YES];
 }
 
-- (IBAction)aboutPithos:(id)sender {
-    [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:self.aboutURL]];
+- (IBAction)showPithosAbout:(id)sender {
+    [NSApp orderFrontStandardAboutPanel:sender];
+    [NSApp activateIgnoringOtherApps:YES];
+}
+
+- (void)sync {
+    if (!activated || ![pithosAccounts count])
+        return;
+    NSUInteger syncIndex;
+    BOOL syncPithosAccountFound = [pithosAccounts containsObject:syncPithosAccount];
+    if (syncPithosAccountFound)
+         syncIndex = [pithosAccounts indexOfObject:syncPithosAccount];
+    
+    PithosAccount *singleSyncPithosAccount = nil;
+    for (PithosAccount *pithosAccount in pithosAccounts) {
+        if (!singleSyncPithosAccount && pithosAccount.active && pithosAccount.syncActive && pithosAccount.syncDaemon) {
+            singleSyncPithosAccount = pithosAccount;
+        } else if (singleSyncPithosAccount && pithosAccount.active && pithosAccount.syncActive && pithosAccount.syncDaemon) {
+            singleSyncPithosAccount = nil;
+            break;
+        }
+    }
+    
+    if (syncPithosAccount && syncPithosAccount.active && syncPithosAccount.syncActive && syncPithosAccount.syncDaemon) {
+        // An active syncDaemon was previously syncing
+        if (singleSyncPithosAccount && [singleSyncPithosAccount isEqualTo:syncPithosAccount]) {
+            // It's the only one, sync again
+            [syncPithosAccount.syncDaemon startDaemon];
+            [syncPithosAccount.syncDaemon sync];
+            return;
+        } else if ([syncPithosAccount.syncDaemon isSyncing]) {
+            // It's still syncing, mark it as late and return
+            [syncPithosAccount.syncDaemon syncLate];
+            return;
+        }
+    }
+    PithosAccount *newSyncPithosAccount = nil;
+    if (syncPithosAccountFound) {
+        for (PithosAccount *pithosAccount in [pithosAccounts objectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(syncIndex + 1, [pithosAccounts count] - syncIndex - 1)]]) {
+            if (pithosAccount.active && pithosAccount.syncActive && pithosAccount.syncDaemon) {
+                newSyncPithosAccount = pithosAccount;
+                break;
+            }
+        }
+        if (!newSyncPithosAccount) {
+            for (PithosAccount *pithosAccount in [pithosAccounts objectsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, syncIndex)]]) {
+                if (pithosAccount.active && pithosAccount.syncActive && pithosAccount.syncDaemon) {
+                    newSyncPithosAccount = pithosAccount;
+                    break;
+                }
+            }
+        }
+    } else {
+        for (PithosAccount *pithosAccount in pithosAccounts) {
+            if (pithosAccount.active && pithosAccount.syncActive && pithosAccount.syncDaemon) {
+                newSyncPithosAccount = pithosAccount;
+                break;
+            }
+        }
+    }        
+    if (newSyncPithosAccount) {
+        // A different syncDaemon is found, sync it
+        self.syncPithosAccount = newSyncPithosAccount;
+        [syncPithosAccount.syncDaemon startDaemon];
+        [syncPithosAccount.syncDaemon sync];
+    } else if (syncPithosAccountFound && syncPithosAccount && syncPithosAccount.active && syncPithosAccount.syncActive && syncPithosAccount.syncDaemon) {
+        [syncPithosAccount.syncDaemon startDaemon];
+        [syncPithosAccount.syncDaemon sync];
+    } else {
+        self.syncPithosAccount = nil;
+    }
+}
+
+- (void)savePithosAccounts:(id)sender {
+    [userDefaults setObject:[NSKeyedArchiver archivedDataWithRootObject:pithosAccounts] forKey:@"pithosAccounts"];
+    [userDefaults synchronize];
+}
+
+- (void)removedPithosAccount:(PithosAccount *)removedPithosAccount {
+    if ([self.currentPithosAccount isEqualTo:removedPithosAccount]) {
+        for (PithosAccount *pithosAccount in pithosAccounts) {
+            if (pithosAccount.active) {
+                self.currentPithosAccount = pithosAccount;
+                self.pithosBrowserController.pithosAccountManager = currentPithosAccount;
+                self.pithosBrowserController.pithos = currentPithosAccount.pithos;
+                break;
+            }
+        }
+        if ([self.currentPithosAccount isEqualTo:removedPithosAccount]) {
+            self.activated = NO;
+            [self.pithosBrowserController.window close];
+            [self.pithosBrowserController resetBrowser];
+            self.currentPithosAccount = [pithosAccounts objectAtIndex:0];
+            self.pithosBrowserController.pithosAccountManager = currentPithosAccount;
+        }
+    }
+    if ([self.syncPithosAccount isEqualTo:removedPithosAccount])
+        self.syncPithosAccount = nil;
+}
+
+- (void)checkForUpdates {
+    @synchronized(self) {
+        if (!checkForUpdatesNotRunning)
+            return;
+        self.checkForUpdatesNotRunning = NO;
+    }
+    ASIHTTPRequest *checkForUpdatesRequest = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"https://code.grnet.gr/projects/pithos-macos/repository/revisions/master/raw/pithos-macos/pithos-macos-Info.plist"]];
+    checkForUpdatesRequest.delegate = self;
+    checkForUpdatesRequest.didFinishSelector = @selector(checkForUpdatesRequestFinished:);
+    checkForUpdatesRequest.didFailSelector = @selector(checkForUpdatesRequestFailed:);
+    checkForUpdatesRequest.timeOutSeconds = 60;
+    checkForUpdatesRequest.numberOfTimesToRetryOnTimeout = 10;
+    [checkForUpdatesRequest startAsynchronous];
 }
 
 #pragma mark -
-#pragma Authentication
-
-- (void)authenticateWithAuthUser:(NSString *)authUser authToken:(NSString *)authToken {
-    NSLog(@"Authentication - storageURLPrefix:%@, authUser:%@, authToken:%@", storageURLPrefix, authUser, authToken);
-    if ([authUser length] && [authToken length]) {
-        [[ASIDownloadCache sharedCache] clearCachedResponsesForStoragePolicy:ASICacheForSessionDurationCacheStoragePolicy];
-        [ASIPithosRequest setAuthURL:storageURLPrefix];
-        [ASIPithosRequest setStorageURLPrefix:storageURLPrefix];
-        [ASIPithosRequest setAuthUser:authUser];
-        [ASIPithosRequest setAuthToken:authToken];
-        [ASIPithosRequest setPublicURLPrefix:publicURLPrefix];
-        
-        [[NSNotificationCenter defaultCenter] postNotificationName:@"PithosAuthenticationCredentialsUpdated" object:self];
+#pragma mark ASIHTTPRequestDelegate
+
+- (void)checkForUpdatesRequestFinished:(ASIHTTPRequest *)request {
+    if (request.responseStatusCode == 200) {
+        NSError *error = nil;
+        NSDictionary *plistDictionary = [NSPropertyListSerialization propertyListWithData:[request.responseString dataUsingEncoding:NSUTF8StringEncoding] 
+                                                                                  options:NSPropertyListImmutable 
+                                                                                   format:NULL 
+                                                                                    error:&error];
+        if (!error) {
+            NSString *currentVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
+            NSString *newVersion = [plistDictionary objectForKey:@"CFBundleVersion"];
+            NSURL *distributionURL = [NSURL URLWithString:[plistDictionary objectForKey:@"PithosDistributionURL"]];
+            if (newVersion && currentVersion && distributionURL && ([newVersion doubleValue] > [currentVersion doubleValue])) {
+                NSAlert *alert = [[NSAlert alloc] init];
+                [alert setMessageText:@"Updates Available"];
+                [alert setInformativeText:@"An updated version is available"];
+                [alert addButtonWithTitle:@"Download"];
+                [alert runModal];
+                [[NSWorkspace sharedWorkspace] openURL:distributionURL];
+            } else if (checkForUpdatesCalledFromMenu) {
+                NSAlert *alert = [[NSAlert alloc] init];
+                [alert setMessageText:@"No Updates Available"];
+                [alert setInformativeText:@"You are running the latest version"];
+                [alert addButtonWithTitle:@"OK"];
+                [alert runModal];
+                checkForUpdatesCalledFromMenu = NO;
+            }
+            [NSTimer scheduledTimerWithTimeInterval:86400 
+                                             target:self 
+                                           selector:@selector(checkForUpdates) 
+                                           userInfo:nil 
+                                            repeats:YES];
+            self.checkForUpdatesNotRunning = YES;
+            return;
+        }
+        DLog(@"Check for update plist error: %@", error);
+     }
+}
+
+- (void)checkForUpdatesRequestFailed:(ASIHTTPRequest *)request {
+    if (checkForUpdatesCalledFromMenu) {
+        NSAlert *alert = [[NSAlert alloc] init];
+        [alert setMessageText:@"Check for Updates Error"];
+        [alert setInformativeText:@"Cannot check for updates now, try again later"];
+        [alert addButtonWithTitle:@"OK"];
+        [alert runModal];
+        checkForUpdatesCalledFromMenu = NO;
+    }
+    [NSTimer scheduledTimerWithTimeInterval:600 
+                                     target:self 
+                                   selector:@selector(checkForUpdates) 
+                                   userInfo:nil 
+                                    repeats:YES];
+    self.checkForUpdatesNotRunning = YES;
+}
+
+#pragma mark -
+#pragma mark Menu Actions
+
+- (void)menuChangePithosAccount:(NSMenuItem *)sender {
+    PithosAccount *pithosAccount = (PithosAccount *)[sender representedObject];
+    if (!pithosAccount.active)
+        return;
+    if (![currentPithosAccount isEqualTo:pithosAccount] && [pithosAccounts containsObject:pithosAccount]) {
+        if ([self.pithosBrowserController operationsPending]) {
+            NSAlert *alert = [[NSAlert alloc] init];
+            [alert setMessageText:@"Pending Operations"];
+            [alert setInformativeText:@"There are pending operations in the browser, do you want to change accounts and cancel them?"];
+            [alert addButtonWithTitle:@"OK"];
+            [alert addButtonWithTitle:@"Cancel"];
+            NSInteger choice = [alert runModal];
+            if (choice == NSAlertSecondButtonReturn) 
+                return;
+        }
+        self.currentPithosAccount = pithosAccount;
+        self.pithosBrowserController.pithosAccountManager = currentPithosAccount;
+        [self showPithosBrowser:self];
+        self.pithosBrowserController.pithos = currentPithosAccount.pithos;
+    }
+}
+
+//- (void)menuChangeSyncActive:(NSMenuItem *)sender {
+//    PithosAccount *pithosAccount = (PithosAccount *)[sender representedObject];
+//    if (!pithosAccount.active)
+//        return;
+//    pithosAccount.syncActive = !pithosAccount.syncActive;
+//    if (self.pithosPreferencesController && [self.pithosPreferencesController.selectedPithosAccount isEqualTo:pithosAccount])
+//        self.pithosPreferencesController.syncActive = pithosAccount.syncActive;
+//    [self savePithosAccounts:self];
+//}
+
+- (IBAction)menuCheckForUpdates:(NSMenuItem *)sender {
+    @synchronized(self) {
+        checkForUpdatesCalledFromMenu = YES;
     }
+    [self checkForUpdates];
 }
 
 @end