// // PithosAccount.m // pithos-macos // // 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 // conditions are met: // // 1. Redistributions of source code must retain the above // copyright notice, this list of conditions and the following // disclaimer. // // 2. Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials // provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS // OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF // USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // // The views and conclusions contained in the software and // documentation are those of the authors and should not be // interpreted as representing official policies, either expressed // or implied, of GRNET S.A. #import "PithosAccount.h" #import "PithosSyncDaemon.h" #import "ASIPithos.h" #import "PithosAccountNode.h" #import "PithosSharingAccountsNode.h" #import "pithos_macosAppDelegate.h" @interface PithosAccount (Internal) - (BOOL)urlIsValid:(NSString *)urlString; @end @implementation PithosAccount @synthesize uniqueName, active, name; @synthesize syncActive, syncDirectoryPath, syncAccountsDictionary, syncLastCompleted, syncDaemon; @synthesize serverURL, versionResource, loginResource, publicResource; @synthesize authUser, authToken, storageURLPrefix, authURL, loginURLPrefix, publicURLPrefix; @synthesize pithos, accountNode, sharingAccountsNode; #pragma mark - #pragma mark Object Lifecycle + (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 { [sharingAccountsNode 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]; [syncAccountsDictionary release]; [syncDirectoryPath release]; [name release]; [uniqueName release]; [super dealloc]; } - (NSString *)description { return [NSString stringWithFormat:@"uniqueName: %@, active: %d, name: %@, syncActive: %d, syncDirectoryPath: %@, syncAccountsDictionary: %@, syncLastCompleted: %@, serverURL: %@, versionResource: %@, loginResource: %@, publicResource: %@, authUser: %@, authToken: %@, storageURLPrefix: %@, authURL: %@, loginURLPrefix: %@, publicURLPrefix: %@", uniqueName, active, name, syncActive, syncDirectoryPath, syncAccountsDictionary, syncLastCompleted, serverURL, versionResource, loginResource, publicResource, authUser, authToken, storageURLPrefix, authURL, loginURLPrefix, publicURLPrefix]; } #pragma mark - #pragma mark Internal - (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 *)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 *)syncAccountsDictionary { if (!syncAccountsDictionary) { syncAccountsDictionary = [[NSMutableDictionary dictionaryWithObject:[NSMutableDictionary dictionaryWithObject:[NSMutableSet set] forKey:@"pithos"] forKey:@""] retain]; } return syncAccountsDictionary; } - (void)setSyncAccountsDictionary:(NSMutableDictionary *)aSyncAccountsDictionary { if (aSyncAccountsDictionary && ![self.syncAccountsDictionary isEqualToDictionary:aSyncAccountsDictionary]) { [syncAccountsDictionary release]; syncAccountsDictionary = [aSyncAccountsDictionary retain]; // Validity of the dictionary is checked when loading from user defaults, and when modified in preferences @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 accountsDictionary:self.syncAccountsDictionary 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; updatePithos = NO; } } return pithos; } - (PithosAccountNode *)accountNode { if (!accountNode) { accountNode = [[PithosAccountNode alloc] initWithPithos:self.pithos]; accountNode.childrenUpdatedNotificationName = nil; accountNode.inheritChildrenUpdatedNotificationName = YES; } return accountNode; } - (PithosSharingAccountsNode *)sharingAccountsNode { if (!sharingAccountsNode) { sharingAccountsNode = [[PithosSharingAccountsNode alloc] initWithPithos:self.pithos]; sharingAccountsNode.childrenUpdatedNotificationName = nil; sharingAccountsNode.inheritChildrenUpdatedNotificationName = YES; } return sharingAccountsNode; } #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]; } if (accountNode) { self.accountNode.pithos = self.pithos; if (self.accountNode.children) { } [self.accountNode refreshInfo]; } if (sharingAccountsNode) { self.sharingAccountsNode.pithos = self.pithos; if (self.sharingAccountsNode.children) { } } } } - (void)loginWithServerURL:(NSString *)aServerURL { self.serverURL = aServerURL; NSProcessInfo *processInfo = [NSProcessInfo processInfo]; NSString *loginURL = [NSString stringWithFormat:@"%@?next=pithos://%d/%@", self.loginURLPrefix, [processInfo processIdentifier], self.name]; NSLog(@"Account: %@\nloginURL: %@", self, loginURL); [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:loginURL]]; } - (void)updateSyncWithSyncActive:(BOOL)aSyncActive syncDirectoryPath:(NSString *)aSyncDirectoryPath syncAccountsDictionary:(NSMutableDictionary *)aSyncAccountsDictionary { self.syncAccountsDictionary = aSyncAccountsDictionary; self.syncDirectoryPath = aSyncDirectoryPath; self.syncActive = aSyncActive; if (syncDaemon) { self.syncDaemon.accountsDictionary = self.syncAccountsDictionary; 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"]; NSDictionary *immutableAccountsDictionary = [decoder decodeObjectForKey:@"syncAccountsDictionary"]; syncAccountsDictionary = [[NSMutableDictionary alloc] initWithCapacity:[immutableAccountsDictionary count]]; for (NSString *accountName in immutableAccountsDictionary) { NSDictionary *immutableContainersDictionary = [immutableAccountsDictionary objectForKey:accountName]; NSMutableDictionary *syncContainersDictionary = [NSMutableDictionary dictionary]; for (NSString *containerName in [immutableAccountsDictionary objectForKey:accountName]) { if (![accountName isEqualToString:@""] || ![[containerName lowercaseString] isEqualToString:@"shared to me"]) [syncContainersDictionary setObject:[NSMutableSet setWithSet:[immutableContainersDictionary objectForKey:containerName]] forKey:containerName]; } if ([syncContainersDictionary count]) [syncAccountsDictionary setObject:syncContainersDictionary forKey:accountName]; } 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:syncAccountsDictionary forKey:@"syncAccountsDictionary"]; [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