5 // Copyright 2012 GRNET S.A. All rights reserved.
7 // Redistribution and use in source and binary forms, with or
8 // without modification, are permitted provided that the following
11 // 1. Redistributions of source code must retain the above
12 // copyright notice, this list of conditions and the following
15 // 2. Redistributions in binary form must reproduce the above
16 // copyright notice, this list of conditions and the following
17 // disclaimer in the documentation and/or other materials
18 // provided with the distribution.
20 // THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
21 // OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
24 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
27 // USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28 // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 // POSSIBILITY OF SUCH DAMAGE.
33 // The views and conclusions contained in the software and
34 // documentation are those of the authors and should not be
35 // interpreted as representing official policies, either expressed
36 // or implied, of GRNET S.A.
38 #import "PithosAccount.h"
39 #import "PithosSyncDaemon.h"
41 #import "PithosAccountNode.h"
42 #import "PithosSharingAccountsNode.h"
43 #import "pithos_macosAppDelegate.h"
45 @interface PithosAccount (Internal)
46 - (BOOL)urlIsValid:(NSString *)urlString;
49 @implementation PithosAccount
50 @synthesize uniqueName, active, name;
51 @synthesize syncActive, syncDirectoryPath, syncAccountsDictionary, syncSkipHidden, syncLastCompleted, syncDaemon;
52 @synthesize serverURL, versionResource, loginResource, publicResource;
53 @synthesize authUser, authToken, storageURLPrefix, authURL, loginURLPrefix, publicURLPrefix;
54 @synthesize pithos, accountNode, sharingAccountsNode;
57 #pragma mark Object Lifecycle
60 PithosAccount *pithosAccount = [[[self alloc] init] autorelease];
61 pithosAccount.uniqueName = [NSString stringWithFormat:@"pithosAccount-%f", [NSDate timeIntervalSinceReferenceDate]];
62 pithosAccount.syncSkipHidden = YES;
63 pithosAccount.versionResource = [NSString stringWithString:@"v1"];
64 pithosAccount.loginResource = [NSString stringWithString:@"login"];
69 [sharingAccountsNode release];
70 [accountNode release];
72 [publicURLPrefix release];
73 [loginURLPrefix release];
75 [storageURLPrefix release];
78 [publicResource release];
79 [loginResource release];
80 [versionResource release];
83 [syncLastCompleted release];
84 [syncAccountsDictionary release];
85 [syncDirectoryPath release];
91 - (NSString *)description {
92 return [NSString stringWithFormat:@"uniqueName: %@, active: %d, name: %@, syncActive: %d, syncDirectoryPath: %@, syncAccountsDictionary: %@, syncSkipHidden: %d, syncLastCompleted: %@, serverURL: %@, versionResource: %@, loginResource: %@, publicResource: %@, authUser: %@, authToken: %@, storageURLPrefix: %@, authURL: %@, loginURLPrefix: %@, publicURLPrefix: %@",
93 uniqueName, active, name, syncActive, syncDirectoryPath, syncAccountsDictionary, syncSkipHidden, syncLastCompleted, serverURL, versionResource, loginResource, publicResource, authUser, authToken, storageURLPrefix, authURL, loginURLPrefix, publicURLPrefix];
99 - (BOOL)urlIsValid:(NSString *)urlString {
101 NSURL *url = [NSURL URLWithString:urlString];
102 if (url && url.scheme && url.host)
109 #pragma mark Properties
112 if (![name length]) {
114 NSDictionary *pithosAccountsDictionary = [(pithos_macosAppDelegate *)[[NSApplication sharedApplication] delegate] pithosAccountsDictionary];
115 NSString *namePrefix = [NSString stringWithString:@"okeanos"];
116 NSUInteger nameSuffix = 1;
117 name = [NSString stringWithString:@"okeanos"];
118 NSString *documentsDirectoryPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
119 NSFileManager *fileManager = [NSFileManager defaultManager];
120 while ([pithosAccountsDictionary objectForKey:name] ||
121 [fileManager fileExistsAtPath:[documentsDirectoryPath stringByAppendingPathComponent:name]]) {
122 name = [NSString stringWithFormat:@"%@%d", namePrefix, ++nameSuffix];
129 - (void)setName:(NSString *)aName {
130 NSMutableDictionary *pithosAccountsDictionary = [(pithos_macosAppDelegate *)[[NSApplication sharedApplication] delegate] pithosAccountsDictionary];
131 if (![self.name isEqualToString:aName] && [aName length] && ![pithosAccountsDictionary objectForKey:aName]) {
132 [pithosAccountsDictionary setObject:self forKey:aName];
133 [pithosAccountsDictionary removeObjectForKey:name];
135 name = [aName retain];
146 - (void)setSyncActive:(BOOL)aSyncActive {
147 syncActive = aSyncActive;
148 if (syncDaemon && !self.syncActive)
149 [syncDaemon resetDaemon];
152 - (NSString *)syncDirectoryPath {
153 if (![syncDirectoryPath length]) {
154 [syncDirectoryPath release];
155 syncDirectoryPath = [[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]
156 stringByAppendingPathComponent:self.name] retain];
158 return syncDirectoryPath;
161 - (void)setSyncDirectoryPath:(NSString *)aSyncDirectoryPath {
162 if (![self.syncDirectoryPath isEqualToString:aSyncDirectoryPath] && [aSyncDirectoryPath length]) {
164 if (![[NSFileManager defaultManager] fileExistsAtPath:aSyncDirectoryPath isDirectory:&isDirectory] || isDirectory) {
165 [syncDirectoryPath release];
166 syncDirectoryPath = [aSyncDirectoryPath retain];
171 @synchronized(self) {
172 resetSyncDaemonLocalState = YES;
173 [syncLastCompleted release];
174 syncLastCompleted = nil;
179 - (NSMutableDictionary *)syncAccountsDictionary {
180 if (!syncAccountsDictionary) {
181 syncAccountsDictionary = [[NSMutableDictionary dictionaryWithObject:[NSMutableDictionary dictionaryWithObject:[NSMutableSet set]
185 return syncAccountsDictionary;
188 - (void)setSyncAccountsDictionary:(NSMutableDictionary *)aSyncAccountsDictionary {
189 if (aSyncAccountsDictionary && ![self.syncAccountsDictionary isEqualToDictionary:aSyncAccountsDictionary]) {
190 [syncAccountsDictionary release];
191 syncAccountsDictionary = [[NSMutableDictionary alloc] initWithCapacity:[aSyncAccountsDictionary count]];
192 for (NSString *accountName in aSyncAccountsDictionary) {
193 NSDictionary *aSyncContainersDictionary = [aSyncAccountsDictionary objectForKey:accountName];
194 NSMutableDictionary *syncContainersDictionary = [NSMutableDictionary dictionary];
195 for (NSString *containerName in aSyncContainersDictionary) {
196 if (![accountName isEqualToString:@""] || ![[containerName lowercaseString] isEqualToString:@"shared to me"])
197 [syncContainersDictionary setObject:[NSMutableSet setWithSet:[aSyncContainersDictionary objectForKey:containerName]]
198 forKey:containerName];
200 if ([syncContainersDictionary count])
201 [syncAccountsDictionary setObject:syncContainersDictionary forKey:accountName];
204 @synchronized(self) {
205 resetSyncDaemonLocalState = YES;
206 [syncLastCompleted release];
207 syncLastCompleted = nil;
212 - (NSDate *)syncLastCompleted {
213 if (self.syncDaemon.lastCompletedSync && ![self.syncDaemon.lastCompletedSync isEqualToDate:syncLastCompleted]) {
214 [syncLastCompleted release];
215 syncLastCompleted = [self.syncDaemon.lastCompletedSync copy];
217 return syncLastCompleted;
220 - (PithosSyncDaemon *)syncDaemon {
221 @synchronized(self) {
222 if (self.syncActive && !syncDaemon)
223 syncDaemon = [[PithosSyncDaemon alloc] initWithDirectoryPath:self.syncDirectoryPath
225 accountsDictionary:self.syncAccountsDictionary
226 skipHidden:self.syncSkipHidden
227 resetLocalState:resetSyncDaemonLocalState];
228 resetSyncDaemonLocalState = NO;
233 - (NSString *)serverURL {
234 if (![self urlIsValid:serverURL]) {
236 serverURL = [[NSString stringWithString:@"https://pithos.okeanos.grnet.gr"] retain];
241 - (void)setServerURL:(NSString *)aServerURL {
242 if (![self.serverURL isEqualToString:aServerURL] && [self urlIsValid:aServerURL]) {
244 serverURL = [aServerURL retain];
245 [storageURLPrefix release];
246 storageURLPrefix = nil;
249 [publicURLPrefix release];
250 publicURLPrefix = nil;
251 [loginURLPrefix release];
252 loginURLPrefix = nil;
254 @synchronized(self) {
256 resetSyncDaemonLocalState = YES;
257 [syncLastCompleted release];
258 syncLastCompleted = nil;
263 - (void)setAuthUser:(NSString *)anAuthUser {
264 if ([anAuthUser length] && ![anAuthUser isEqualToString:authUser]) {
266 authUser = [anAuthUser retain];
268 @synchronized(self) {
270 resetSyncDaemonLocalState = YES;
271 [syncLastCompleted release];
272 syncLastCompleted = nil;
278 - (void)setAuthToken:(NSString *)anAuthToken {
279 if ([anAuthToken length] && ![anAuthToken isEqualToString:authToken]) {
281 authToken = [anAuthToken retain];
283 @synchronized(self) {
289 - (NSString *)storageURLPrefix {
290 if (![self urlIsValid:storageURLPrefix]) {
291 [storageURLPrefix release];
293 storageURLPrefix = [[self.serverURL stringByAppendingFormat:@"/%@", versionResource] retain];
295 storageURLPrefix = [self.serverURL copy];
297 return storageURLPrefix;
300 - (void)setStorageURLPrefix:(NSString *)aStorageURLPrefix {
301 if (![self.storageURLPrefix isEqualToString:aStorageURLPrefix] && [self urlIsValid:aStorageURLPrefix]) {
302 [storageURLPrefix release];
303 storageURLPrefix = [aStorageURLPrefix retain];
307 - (NSString *)authURL {
308 if (![self urlIsValid:authURL]) {
311 authURL = [[self.serverURL stringByAppendingFormat:@"/%@", versionResource] retain];
313 authURL = [self.serverURL copy];
318 - (void)setAuthURL:(NSString *)anAuthURL {
319 if (![self.authURL isEqualToString:anAuthURL] && [self urlIsValid:anAuthURL]) {
321 authURL = [anAuthURL retain];
325 - (NSString *)publicURLPrefix {
326 if (![self urlIsValid:publicURLPrefix]) {
327 [publicURLPrefix release];
329 publicURLPrefix = [[self.serverURL stringByAppendingFormat:@"/%@", publicResource] retain];
331 publicURLPrefix = [self.serverURL copy];
333 return publicURLPrefix;
336 - (void)setPublicURLPrefix:(NSString *)aPublicURLPrefix {
337 if (![self.publicURLPrefix isEqualToString:aPublicURLPrefix] && [self urlIsValid:aPublicURLPrefix]) {
338 [publicURLPrefix release];
339 publicURLPrefix = [aPublicURLPrefix retain];
343 - (NSString *)loginURLPrefix {
344 if (![self urlIsValid:loginURLPrefix]) {
345 [loginURLPrefix release];
347 loginURLPrefix = [[self.serverURL stringByAppendingFormat:@"/%@", loginResource] retain];
349 loginURLPrefix = [self.serverURL copy];
351 return loginURLPrefix;
354 - (void)setLoginURLPrefix:(NSString *)aLoginURLPrefix {
355 if (![self.loginURLPrefix isEqualToString:aLoginURLPrefix] && [self urlIsValid:aLoginURLPrefix]) {
356 [loginURLPrefix release];
357 loginURLPrefix = [aLoginURLPrefix retain];
361 - (ASIPithos *)pithos {
362 @synchronized(self) {
363 if (!pithos || updatePithos) {
365 pithos = [[ASIPithos pithos] retain];
366 pithos.authUser = authUser;
367 pithos.authToken = authToken;
368 pithos.storageURLPrefix = self.storageURLPrefix;
369 pithos.authURL = self.authURL;
370 pithos.publicURLPrefix = self.publicURLPrefix;
377 - (PithosAccountNode *)accountNode {
379 accountNode = [[PithosAccountNode alloc] initWithPithos:self.pithos];
380 accountNode.childrenUpdatedNotificationName = nil;
381 accountNode.inheritChildrenUpdatedNotificationName = YES;
386 - (PithosSharingAccountsNode *)sharingAccountsNode {
387 if (!sharingAccountsNode) {
388 sharingAccountsNode = [[PithosSharingAccountsNode alloc] initWithPithos:self.pithos];
389 sharingAccountsNode.childrenUpdatedNotificationName = nil;
390 sharingAccountsNode.inheritChildrenUpdatedNotificationName = YES;
392 return sharingAccountsNode;
398 - (void)authenticateWithServerURL:(NSString *)aServerURL authUser:(NSString *)anAuthUser authToken:(NSString *)anAuthToken {
399 self.serverURL = aServerURL;
400 self.authUser = anAuthUser;
401 self.authToken = anAuthToken;
402 NSLog(@"Account: %@\nauthentication", self);
403 if (![authUser length] || ![authToken length]) {
405 self.syncActive = NO;
406 // XXX Show preferences with self as the selected account?
410 self.syncDaemon.pithos = self.pithos;
412 [self.syncDaemon startDaemon];
415 self.accountNode.pithos = self.pithos;
416 if (sharingAccountsNode)
417 self.sharingAccountsNode.pithos = self.pithos;
419 if (self.accountNode.children) {
421 [self.accountNode refreshInfo];
423 if (sharingAccountsNode && self.sharingAccountsNode.children) {
428 - (void)loginWithServerURL:(NSString *)aServerURL {
429 self.serverURL = aServerURL;
430 NSProcessInfo *processInfo = [NSProcessInfo processInfo];
431 NSString *loginURL = [NSString stringWithFormat:@"%@?next=pithos://%d/%@&force=",
432 self.loginURLPrefix, [processInfo processIdentifier], self.name];
433 NSLog(@"Account: %@\nloginURL: %@", self, loginURL);
434 [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:loginURL]];
437 - (void)updateSyncWithSyncActive:(BOOL)aSyncActive
438 syncDirectoryPath:(NSString *)aSyncDirectoryPath
439 syncAccountsDictionary:(NSMutableDictionary *)aSyncAccountsDictionary
440 syncSkipHidden:(BOOL)aSyncSkipHidden {
441 self.syncAccountsDictionary = aSyncAccountsDictionary;
442 self.syncDirectoryPath = aSyncDirectoryPath;
443 self.syncSkipHidden = aSyncSkipHidden;
444 self.syncActive = aSyncActive;
446 self.syncDaemon.accountsDictionary = self.syncAccountsDictionary;
447 self.syncDaemon.directoryPath = self.syncDirectoryPath;
448 self.syncDaemon.skipHidden = self.syncSkipHidden;
450 [self.syncDaemon startDaemon];
455 #pragma mark NSCoding
457 - (id)initWithCoder:(NSCoder *)decoder {
458 if ((self = [super init])) {
459 self.uniqueName = [decoder decodeObjectForKey:@"uniqueName"];
460 self.active = [decoder decodeBoolForKey:@"active"];
461 name = [[decoder decodeObjectForKey:@"name"] retain];
463 self.syncActive = [decoder decodeBoolForKey:@"syncActive"];
464 self.syncDirectoryPath = [decoder decodeObjectForKey:@"syncDirectoryPath"];
465 self.syncAccountsDictionary = [decoder decodeObjectForKey:@"syncAccountsDictionary"];
466 self.syncSkipHidden = [decoder decodeBoolForKey:@"syncSkipHidden"];
467 self.syncLastCompleted = [decoder decodeObjectForKey:@"syncLastCompleted"];
469 self.serverURL = [decoder decodeObjectForKey:@"serverURL"];
470 self.versionResource = [decoder decodeObjectForKey:@"versionResource"];
471 self.loginResource = [decoder decodeObjectForKey:@"loginResource"];
472 self.publicResource = [decoder decodeObjectForKey:@"publicResource"];
474 self.authUser = [decoder decodeObjectForKey:@"authUser"];
475 self.authToken = [decoder decodeObjectForKey:@"authToken"];
476 self.storageURLPrefix = [decoder decodeObjectForKey:@"storageURLPrefix"];
477 self.authURL = [decoder decodeObjectForKey:@"authURL"];
478 self.publicURLPrefix = [decoder decodeObjectForKey:@"publicURLPrefix"];
479 self.loginURLPrefix = [decoder decodeObjectForKey:@"loginURLPrefix"];
481 if (![authUser length] || ![authToken length] || ![self.storageURLPrefix length])
484 resetSyncDaemonLocalState = NO;
489 - (void)encodeWithCoder:(NSCoder *)encoder {
490 [encoder encodeObject:uniqueName forKey:@"uniqueName"];
491 [encoder encodeBool:active forKey:@"active"];
492 [encoder encodeObject:name forKey:@"name"];
494 [encoder encodeBool:syncActive forKey:@"syncActive"];
495 [encoder encodeObject:syncDirectoryPath forKey:@"syncDirectoryPath"];
496 [encoder encodeObject:syncAccountsDictionary forKey:@"syncAccountsDictionary"];
497 [encoder encodeBool:syncSkipHidden forKey:@"syncSkipHidden"];
498 [encoder encodeObject:self.syncLastCompleted forKey:@"syncLastCompleted"];
500 [encoder encodeObject:serverURL forKey:@"serverURL"];
501 [encoder encodeObject:versionResource forKey:@"versionResource"];
502 [encoder encodeObject:publicResource forKey:@"publicResource"];
503 [encoder encodeObject:loginResource forKey:@"loginResource"];
505 [encoder encodeObject:authUser forKey:@"authUser"];
506 [encoder encodeObject:authToken forKey:@"authToken"];
507 [encoder encodeObject:storageURLPrefix forKey:@"storageURLPrefix"];
508 [encoder encodeObject:authURL forKey:@"authURL"];
509 [encoder encodeObject:publicURLPrefix forKey:@"publicURLPrefix"];
510 [encoder encodeObject:loginURLPrefix forKey:@"loginURLPrefix"];