//
-// PithosNode.m
+// PithosAccount.m
// pithos-macos
//
-// Copyright 2011 GRNET S.A. All rights reserved.
+// Copyright 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 "PithosAccount.h"
-#import "PithosContainer.h"
-#import "ASIPithosAccountRequest.h"
-#import "ASIPithosContainer.h"
-#import "ASIDownloadCache.h"
+#import "PithosSyncDaemon.h"
+#import "ASIPithos.h"
+#import "PithosAccountNode.h"
+#import "pithos_macosAppDelegate.h"
+
+@interface PithosAccount (Internal)
+- (BOOL)urlIsValid:(NSString *)urlString;
+@end
@implementation PithosAccount
+@synthesize uniqueName, active, name;
+@synthesize syncActive, syncDirectoryPath, syncContainersDictionary, syncLastCompleted, syncDaemon;
+@synthesize serverURL, versionResource, loginResource, publicResource;
+@synthesize authUser, authToken, storageURLPrefix, authURL, loginURLPrefix, publicURLPrefix;
+@synthesize pithos, accountNode;
#pragma mark -
#pragma mark Object Lifecycle
-- (id)init {
- if ((self = [super init])) {
- refreshing = NO;
- }
- return self;
++ (id)pithosAccount {
+ PithosAccount *pithosAccount = [[[self alloc] init] autorelease];
+ pithosAccount.uniqueName = [NSString stringWithFormat:@"pithosAccount-%f", [NSDate timeIntervalSinceReferenceDate]];
+ pithosAccount.versionResource = [NSString stringWithString:@"v1"];
+ pithosAccount.loginResource = [NSString stringWithString:@"login"];
+ return pithosAccount;
}
- (void)dealloc {
- [containers release];
+ [accountNode release];
+ [pithos release];
+ [publicURLPrefix release];
+ [loginURLPrefix release];
+ [authURL release];
+ [storageURLPrefix release];
+ [authToken release];
+ [authUser release];
+ [publicResource release];
+ [loginResource release];
+ [versionResource release];
+ [serverURL release];
+ [syncDaemon release];
+ [syncLastCompleted release];
+ [syncContainersDictionary release];
+ [syncDirectoryPath release];
+ [name release];
+ [uniqueName release];
[super dealloc];
}
+- (NSString *)description {
+ return [NSString stringWithFormat:@"uniqueName: %@, active: %d, name: %@, syncActive: %d, syncDirectoryPath: %@, syncContainersDictionary: %@, syncLastCompleted: %@, serverURL: %@, versionResource: %@, loginResource: %@, publicResource: %@, authUser: %@, authToken: %@, storageURLPrefix: %@, authURL: %@, loginURLPrefix: %@, publicURLPrefix: %@",
+ uniqueName, active, name, syncActive, syncDirectoryPath, syncContainersDictionary, syncLastCompleted, serverURL, versionResource, loginResource, publicResource, authUser, authToken, storageURLPrefix, authURL, loginURLPrefix, publicURLPrefix];
+}
+
#pragma mark -
-#pragma mark ASIHTTPRequestDelegate
+#pragma mark Internal
-- (void)requestFinished:(ASIPithosAccountRequest *)accountRequest {
- NSLog(@"URL: %@", [accountRequest url]);
- NSLog(@"cached: %d", [accountRequest didUseCachedResponse]);
-
- NSArray *someContainers = [accountRequest containers];
- if (containers == nil) {
- containers = [[NSMutableArray alloc] initWithArray:someContainers];
- } else {
- [containers addObjectsFromArray:someContainers];
- }
- if ([someContainers count] < 10000) {
- if (!accountRequest.didUseCachedResponse || ([containers count] != [someContainers count]) || !children) {
- // Save new children
- NSLog(@"using newChildren");
- NSMutableArray *newChildren = [NSMutableArray array];
- for (ASIPithosContainer *container in containers) {
- PithosContainer *node = [[[PithosContainer alloc] initWithContainerName:container.name] autorelease];
- if (children) {
- NSUInteger oldIndex = [children indexOfObject:node];
- if (oldIndex != NSNotFound)
- // Use the same pointer value, if possible
- node = [children objectAtIndex:oldIndex];
- }
- [newChildren addObject:node];
- }
- [children release];
- children = [newChildren retain];
- }
- // Else cache was used and all results were fetched during this request, so previousChildren can be reused
- [containers release];
- containers = nil;
- // XXX sort children based on preferences
- refreshing = NO;
- // Notify observers that children are updated
- [[NSNotificationCenter defaultCenter] postNotificationName:@"PithosNodeChildrenUpdated" object:self];
- } else {
- // Do an additional request to fetch more objects
- ASIPithosAccountRequest *newAccountRequest = [ASIPithosAccountRequest listContainersRequestWithLimit:0
- marker:[[someContainers lastObject] name]
- shared:NO
- until:nil];
- newAccountRequest.delegate = self;
- newAccountRequest.downloadCache = [ASIDownloadCache sharedCache];
- [newAccountRequest startAsynchronous];
- }
-}
-
-- (void)requestFailed:(ASIPithosAccountRequest *)accountRequest {
- // XXX do something on error, cleanup
- NSLog(@"error:%@", [accountRequest error]);
- [containers release];
- containers = nil;
- childrenDirty = YES;
- refreshing = NO;
+- (BOOL)urlIsValid:(NSString *)urlString {
+ if (urlString) {
+ NSURL *url = [NSURL URLWithString:urlString];
+ if (url && url.scheme && url.host)
+ return YES;
+ }
+ return NO;
}
#pragma mark -
#pragma mark Properties
-- (NSString *)url {
- if (url == nil)
- url = [[ASIPithosRequest storageURL] copy];
- return url;
-}
-
-- (NSArray *)children {
- if (childrenDirty) {
- @synchronized (self) {
- if (!refreshing) {
- refreshing = YES;
- childrenDirty = NO;
- ASIPithosAccountRequest *accountRequest = [ASIPithosAccountRequest listContainersRequestWithLimit:0
- marker:nil
- shared:NO
- until:nil];
- accountRequest.delegate = self;
- accountRequest.downloadCache = [ASIDownloadCache sharedCache];
- [accountRequest startAsynchronous];
+- (NSString *)name {
+ if (![name length]) {
+ [name release];
+ NSDictionary *pithosAccountsDictionary = [(pithos_macosAppDelegate *)[[NSApplication sharedApplication] delegate] pithosAccountsDictionary];
+ NSString *namePrefix = [NSString stringWithString:@"okeanos"];
+ NSUInteger nameSuffix = 1;
+ name = [NSString stringWithString:@"okeanos"];
+ NSString *documentsDirectoryPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
+ NSFileManager *fileManager = [NSFileManager defaultManager];
+ while ([pithosAccountsDictionary objectForKey:name] ||
+ [fileManager fileExistsAtPath:[documentsDirectoryPath stringByAppendingPathComponent:name]]) {
+ name = [NSString stringWithFormat:@"%@%d", namePrefix, ++nameSuffix];
+ }
+ [name retain];
+ }
+ return name;
+}
+
+- (void)setName:(NSString *)aName {
+ NSMutableDictionary *pithosAccountsDictionary = [(pithos_macosAppDelegate *)[[NSApplication sharedApplication] delegate] pithosAccountsDictionary];
+ if (![self.name isEqualToString:aName] && [aName length] && ![pithosAccountsDictionary objectForKey:aName]) {
+ [pithosAccountsDictionary setObject:self forKey:aName];
+ [pithosAccountsDictionary removeObjectForKey:name];
+ [name release];
+ name = [aName retain];
+ }
+}
+
+- (BOOL)syncActive {
+ if (active)
+ return syncActive;
+ else
+ return NO;
+}
+
+- (void)setSyncActive:(BOOL)aSyncActive {
+ syncActive = aSyncActive;
+ if (syncDaemon && !self.syncActive)
+ [syncDaemon resetDaemon];
+}
+
+- (NSString *)syncDirectoryPath {
+ if (![syncDirectoryPath length]) {
+ [syncDirectoryPath release];
+ syncDirectoryPath = [[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]
+ stringByAppendingPathComponent:self.name] retain];
+ }
+ return syncDirectoryPath;
+}
+
+- (void)setSyncDirectoryPath:(NSString *)aSyncDirectoryPath {
+ if (![self.syncDirectoryPath isEqualToString:aSyncDirectoryPath] && [aSyncDirectoryPath length]) {
+ BOOL isDirectory;
+ if (![[NSFileManager defaultManager] fileExistsAtPath:aSyncDirectoryPath isDirectory:&isDirectory] || isDirectory) {
+ [syncDirectoryPath release];
+ syncDirectoryPath = [aSyncDirectoryPath retain];
+ } else {
+ return;
+ }
+
+ @synchronized(self) {
+ resetSyncDaemonLocalState = YES;
+ [syncLastCompleted release];
+ syncLastCompleted = nil;
+ }
+ }
+}
+
+- (NSMutableDictionary *)syncContainersDictionary {
+ if (!syncContainersDictionary) {
+ syncContainersDictionary = [[NSMutableDictionary dictionaryWithObject:[NSMutableArray array]
+ forKey:@"pithos"] retain];
+ }
+ return syncContainersDictionary;
+}
+
+- (void)setSyncContainersDictionary:(NSMutableDictionary *)aSyncContainersDictionary {
+ if (![self.syncContainersDictionary isEqualTo:syncContainersDictionary]) {
+ [syncContainersDictionary release];
+ syncContainersDictionary = [aSyncContainersDictionary retain];
+ // XXX check for proper dictionary here
+
+ @synchronized(self) {
+ resetSyncDaemonLocalState = YES;
+ [syncLastCompleted release];
+ syncLastCompleted = nil;
+ }
+ }
+}
+
+- (NSDate *)syncLastCompleted {
+ if (self.syncDaemon.lastCompletedSync && ![self.syncDaemon.lastCompletedSync isEqualToDate:syncLastCompleted]) {
+ [syncLastCompleted release];
+ syncLastCompleted = [self.syncDaemon.lastCompletedSync copy];
+ }
+ return syncLastCompleted;
+}
+
+- (PithosSyncDaemon *)syncDaemon {
+ @synchronized(self) {
+ if (self.syncActive && !syncDaemon)
+ syncDaemon = [[PithosSyncDaemon alloc] initWithDirectoryPath:self.syncDirectoryPath
+ pithosAccount:self
+ containersDictionary:self.syncContainersDictionary
+ resetLocalState:resetSyncDaemonLocalState];
+ resetSyncDaemonLocalState = NO;
+ }
+ return syncDaemon;
+}
+
+- (NSString *)serverURL {
+ if (![self urlIsValid:serverURL]) {
+ [serverURL release];
+ serverURL = [[NSString stringWithString:@"https://pithos.okeanos.grnet.gr"] retain];
+ }
+ return serverURL;
+}
+
+- (void)setServerURL:(NSString *)aServerURL {
+ if (![self.serverURL isEqualToString:aServerURL] && [self urlIsValid:aServerURL]) {
+ [serverURL release];
+ serverURL = [aServerURL retain];
+ [storageURLPrefix release];
+ storageURLPrefix = nil;
+ [authURL release];
+ authURL = nil;
+ [publicURLPrefix release];
+ publicURLPrefix = nil;
+ [loginURLPrefix release];
+ loginURLPrefix = nil;
+
+ @synchronized(self) {
+ updatePithos = YES;
+ resetSyncDaemonLocalState = YES;
+ [syncLastCompleted release];
+ syncLastCompleted = nil;
+ }
+ }
+}
+
+- (void)setAuthUser:(NSString *)anAuthUser {
+ if ([anAuthUser length] && ![anAuthUser isEqualToString:authUser]) {
+ [authUser release];
+ authUser = [anAuthUser retain];
+
+ @synchronized(self) {
+ updatePithos = YES;
+ resetSyncDaemonLocalState = YES;
+ [syncLastCompleted release];
+ syncLastCompleted = nil;
+
+ }
+ }
+}
+
+- (void)setAuthToken:(NSString *)anAuthToken {
+ if ([anAuthToken length] && ![anAuthToken isEqualToString:authToken]) {
+ [authToken release];
+ authToken = [anAuthToken retain];
+
+ @synchronized(self) {
+ updatePithos = YES;
+ }
+ }
+}
+
+- (NSString *)storageURLPrefix {
+ if (![self urlIsValid:storageURLPrefix]) {
+ [storageURLPrefix release];
+ if (versionResource)
+ storageURLPrefix = [[self.serverURL stringByAppendingFormat:@"/%@", versionResource] retain];
+ else
+ storageURLPrefix = [self.serverURL copy];
+ }
+ return storageURLPrefix;
+}
+
+- (void)setStorageURLPrefix:(NSString *)aStorageURLPrefix {
+ if (![self.storageURLPrefix isEqualToString:aStorageURLPrefix] && [self urlIsValid:aStorageURLPrefix]) {
+ [storageURLPrefix release];
+ storageURLPrefix = [aStorageURLPrefix retain];
+ }
+}
+
+- (NSString *)authURL {
+ if (![self urlIsValid:authURL]) {
+ [authURL release];
+ if (versionResource)
+ authURL = [[self.serverURL stringByAppendingFormat:@"/%@", versionResource] retain];
+ else
+ authURL = [self.serverURL copy];
+ }
+ return authURL;
+}
+
+- (void)setAuthURL:(NSString *)anAuthURL {
+ if (![self.authURL isEqualToString:anAuthURL] && [self urlIsValid:anAuthURL]) {
+ [authURL release];
+ authURL = [anAuthURL retain];
+ }
+}
+
+- (NSString *)publicURLPrefix {
+ if (![self urlIsValid:publicURLPrefix]) {
+ [publicURLPrefix release];
+ if (publicResource)
+ publicURLPrefix = [[self.serverURL stringByAppendingFormat:@"/%@", publicResource] retain];
+ else
+ publicURLPrefix = [self.serverURL copy];
+ }
+ return publicURLPrefix;
+}
+
+- (void)setPublicURLPrefix:(NSString *)aPublicURLPrefix {
+ if (![self.publicURLPrefix isEqualToString:aPublicURLPrefix] && [self urlIsValid:aPublicURLPrefix]) {
+ [publicURLPrefix release];
+ publicURLPrefix = [aPublicURLPrefix retain];
+ }
+}
+
+- (NSString *)loginURLPrefix {
+ if (![self urlIsValid:loginURLPrefix]) {
+ [loginURLPrefix release];
+ if (loginResource)
+ loginURLPrefix = [[self.serverURL stringByAppendingFormat:@"/%@", loginResource] retain];
+ else
+ loginURLPrefix = [self.serverURL copy];
+ }
+ return loginURLPrefix;
+}
+
+- (void)setLoginURLPrefix:(NSString *)aLoginURLPrefix {
+ if (![self.loginURLPrefix isEqualToString:aLoginURLPrefix] && [self urlIsValid:aLoginURLPrefix]) {
+ [loginURLPrefix release];
+ loginURLPrefix = [aLoginURLPrefix retain];
+ }
+}
+
+- (ASIPithos *)pithos {
+ @synchronized(self) {
+ if (!pithos || updatePithos) {
+ [pithos release];
+ pithos = [[ASIPithos pithos] retain];
+ pithos.authUser = authUser;
+ pithos.authToken = authToken;
+ pithos.storageURLPrefix = self.storageURLPrefix;
+ pithos.authURL = self.authURL;
+ pithos.publicURLPrefix = self.publicURLPrefix;
+
+ if (accountNode && ![accountNode.pithos isEqualTo:pithos]) {
+ accountNode.pithos = pithos;
+ if (active)
+ [accountNode refreshInfo];
}
+
+ updatePithos = NO;
}
}
- return children;
+ return pithos;
}
-- (NSString *)displayName {
- if (displayName == nil) {
- displayName = [[NSString alloc] initWithString:@"account"];
+- (PithosAccountNode *)accountNode {
+ if (!accountNode) {
+ accountNode = [[PithosAccountNode alloc] initWithPithos:self.pithos];
}
- return displayName;
+ return accountNode;
+}
+
+#pragma mark -
+#pragma mark Actions
+
+- (void)authenticateWithServerURL:(NSString *)aServerURL authUser:(NSString *)anAuthUser authToken:(NSString *)anAuthToken {
+ self.serverURL = aServerURL;
+ self.authUser = anAuthUser;
+ self.authToken = anAuthToken;
+ NSLog(@"Account: %@\nauthentication", self);
+ if (![authUser length] || ![authToken length]) {
+ self.active = NO;
+ self.syncActive = NO;
+ // XXX Show preferences with self as the selected account?
+ } else {
+ self.active = YES;
+ if (syncDaemon) {
+ self.syncDaemon.pithos = self.pithos;
+ if (self.syncActive)
+ [self.syncDaemon startDaemon];
+ }
+ }
+}
+
+- (void)loginWithServerURL:(NSString *)aServerURL {
+ self.serverURL = aServerURL;
+ NSProcessInfo *processInfo = [NSProcessInfo processInfo];
+ NSString *loginURL = [NSString stringWithFormat:@"%@?next=pithos://%@_%d/%@",
+ self.loginURLPrefix, [processInfo processName], [processInfo processIdentifier], self.name];
+ NSLog(@"Account: %@\nloginURL: %@", self, loginURL);
+ [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:loginURL]];
}
-- (NSImage *)icon {
- return nil;
+- (void)updateSyncWithSyncActive:(BOOL)aSyncActive syncDirectoryPath:(NSString *)aSyncDirectoryPath {
+ self.syncDirectoryPath = aSyncDirectoryPath;
+ self.syncActive = aSyncActive;
+ if (syncDaemon) {
+ self.syncDaemon.directoryPath = self.syncDirectoryPath;
+ if (self.syncActive)
+ [self.syncDaemon startDaemon];
+ }
}
+
+#pragma mark -
+#pragma mark NSCoding
+
+- (id)initWithCoder:(NSCoder *)decoder {
+ if ((self = [super init])) {
+ self.uniqueName = [decoder decodeObjectForKey:@"uniqueName"];
+ self.active = [decoder decodeBoolForKey:@"active"];
+ name = [[decoder decodeObjectForKey:@"name"] retain];
+
+ self.syncActive = [decoder decodeBoolForKey:@"syncActive"];
+ self.syncDirectoryPath = [decoder decodeObjectForKey:@"syncDirectoryPath"];
+ self.syncContainersDictionary = [decoder decodeObjectForKey:@"syncContainersDictionary"];
+ self.syncLastCompleted = [decoder decodeObjectForKey:@"syncLastCompleted"];
+
+ self.serverURL = [decoder decodeObjectForKey:@"serverURL"];
+ self.versionResource = [decoder decodeObjectForKey:@"versionResource"];
+ self.loginResource = [decoder decodeObjectForKey:@"loginResource"];
+ self.publicResource = [decoder decodeObjectForKey:@"publicResource"];
+
+ self.authUser = [decoder decodeObjectForKey:@"authUser"];
+ self.authToken = [decoder decodeObjectForKey:@"authToken"];
+ self.storageURLPrefix = [decoder decodeObjectForKey:@"storageURLPrefix"];
+ self.authURL = [decoder decodeObjectForKey:@"authURL"];
+ self.publicURLPrefix = [decoder decodeObjectForKey:@"publicURLPrefix"];
+ self.loginURLPrefix = [decoder decodeObjectForKey:@"loginURLPrefix"];
+
+ if (![authUser length] || ![authToken length] || ![self.storageURLPrefix length])
+ self.active = NO;
+
+ resetSyncDaemonLocalState = NO;
+ }
+ return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)encoder {
+ [encoder encodeObject:uniqueName forKey:@"uniqueName"];
+ [encoder encodeBool:active forKey:@"active"];
+ [encoder encodeObject:name forKey:@"name"];
+
+ [encoder encodeBool:syncActive forKey:@"syncActive"];
+ [encoder encodeObject:syncDirectoryPath forKey:@"syncDirectoryPath"];
+ [encoder encodeObject:syncContainersDictionary forKey:@"syncContainersDictionary"];
+ [encoder encodeObject:self.syncLastCompleted forKey:@"syncLastCompleted"];
+
+ [encoder encodeObject:serverURL forKey:@"serverURL"];
+ [encoder encodeObject:versionResource forKey:@"versionResource"];
+ [encoder encodeObject:publicResource forKey:@"publicResource"];
+ [encoder encodeObject:loginResource forKey:@"loginResource"];
+ [encoder encodeObject:authUser forKey:@"authUser"];
+ [encoder encodeObject:authToken forKey:@"authToken"];
+ [encoder encodeObject:storageURLPrefix forKey:@"storageURLPrefix"];
+ [encoder encodeObject:authURL forKey:@"authURL"];
+ [encoder encodeObject:publicURLPrefix forKey:@"publicURLPrefix"];
+ [encoder encodeObject:loginURLPrefix forKey:@"loginURLPrefix"];
+}
+
@end