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 "pithos_macosAppDelegate.h"
44 @interface PithosAccount (Internal)
45 - (BOOL)urlIsValid:(NSString *)urlString;
48 @implementation PithosAccount
49 @synthesize uniqueName, active, name;
50 @synthesize syncActive, syncDirectoryPath, syncContainersDictionary, syncLastCompleted, syncDaemon;
51 @synthesize serverURL, versionResource, loginResource, publicResource;
52 @synthesize authUser, authToken, storageURLPrefix, authURL, loginURLPrefix, publicURLPrefix;
53 @synthesize pithos, accountNode;
56 #pragma mark Object Lifecycle
59 PithosAccount *pithosAccount = [[[self alloc] init] autorelease];
60 pithosAccount.uniqueName = [NSString stringWithFormat:@"pithosAccount-%f", [NSDate timeIntervalSinceReferenceDate]];
61 pithosAccount.versionResource = [NSString stringWithString:@"v1"];
62 pithosAccount.loginResource = [NSString stringWithString:@"login"];
67 [accountNode release];
69 [publicURLPrefix release];
70 [loginURLPrefix release];
72 [storageURLPrefix release];
75 [publicResource release];
76 [loginResource release];
77 [versionResource release];
80 [syncLastCompleted release];
81 [syncContainersDictionary release];
82 [syncDirectoryPath release];
88 - (NSString *)description {
89 return [NSString stringWithFormat:@"uniqueName: %@, active: %d, name: %@, syncActive: %d, syncDirectoryPath: %@, syncContainersDictionary: %@, syncLastCompleted: %@, serverURL: %@, versionResource: %@, loginResource: %@, publicResource: %@, authUser: %@, authToken: %@, storageURLPrefix: %@, authURL: %@, loginURLPrefix: %@, publicURLPrefix: %@",
90 uniqueName, active, name, syncActive, syncDirectoryPath, syncContainersDictionary, syncLastCompleted, serverURL, versionResource, loginResource, publicResource, authUser, authToken, storageURLPrefix, authURL, loginURLPrefix, publicURLPrefix];
96 - (BOOL)urlIsValid:(NSString *)urlString {
98 NSURL *url = [NSURL URLWithString:urlString];
99 if (url && url.scheme && url.host)
106 #pragma mark Properties
109 if (![name length]) {
111 NSDictionary *pithosAccountsDictionary = [(pithos_macosAppDelegate *)[[NSApplication sharedApplication] delegate] pithosAccountsDictionary];
112 NSString *namePrefix = [NSString stringWithString:@"okeanos"];
113 NSUInteger nameSuffix = 1;
114 name = [NSString stringWithString:@"okeanos"];
115 NSString *documentsDirectoryPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
116 NSFileManager *fileManager = [NSFileManager defaultManager];
117 while ([pithosAccountsDictionary objectForKey:name] ||
118 [fileManager fileExistsAtPath:[documentsDirectoryPath stringByAppendingPathComponent:name]]) {
119 name = [NSString stringWithFormat:@"%@%d", namePrefix, ++nameSuffix];
126 - (void)setName:(NSString *)aName {
127 NSMutableDictionary *pithosAccountsDictionary = [(pithos_macosAppDelegate *)[[NSApplication sharedApplication] delegate] pithosAccountsDictionary];
128 if (![self.name isEqualToString:aName] && [aName length] && ![pithosAccountsDictionary objectForKey:aName]) {
129 [pithosAccountsDictionary setObject:self forKey:aName];
130 [pithosAccountsDictionary removeObjectForKey:name];
132 name = [aName retain];
143 - (void)setSyncActive:(BOOL)aSyncActive {
144 syncActive = aSyncActive;
145 if (syncDaemon && !self.syncActive)
146 [syncDaemon resetDaemon];
149 - (NSString *)syncDirectoryPath {
150 if (![syncDirectoryPath length]) {
151 [syncDirectoryPath release];
152 syncDirectoryPath = [[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]
153 stringByAppendingPathComponent:self.name] retain];
155 return syncDirectoryPath;
158 - (void)setSyncDirectoryPath:(NSString *)aSyncDirectoryPath {
159 if (![self.syncDirectoryPath isEqualToString:aSyncDirectoryPath] && [aSyncDirectoryPath length]) {
161 if (![[NSFileManager defaultManager] fileExistsAtPath:aSyncDirectoryPath isDirectory:&isDirectory] || isDirectory) {
162 [syncDirectoryPath release];
163 syncDirectoryPath = [aSyncDirectoryPath retain];
168 @synchronized(self) {
169 resetSyncDaemonLocalState = YES;
170 [syncLastCompleted release];
171 syncLastCompleted = nil;
176 - (NSMutableDictionary *)syncContainersDictionary {
177 if (!syncContainersDictionary) {
178 syncContainersDictionary = [[NSMutableDictionary dictionaryWithObject:[NSMutableArray array]
179 forKey:@"pithos"] retain];
181 return syncContainersDictionary;
184 - (void)setSyncContainersDictionary:(NSMutableDictionary *)aSyncContainersDictionary {
185 if (aSyncContainersDictionary && ![self.syncContainersDictionary isEqualToDictionary:aSyncContainersDictionary]) {
186 [syncContainersDictionary release];
187 syncContainersDictionary = [aSyncContainersDictionary retain];
188 // XXX maybe check also here the validity of the dictionary?
190 @synchronized(self) {
191 resetSyncDaemonLocalState = YES;
192 [syncLastCompleted release];
193 syncLastCompleted = nil;
198 - (NSDate *)syncLastCompleted {
199 if (self.syncDaemon.lastCompletedSync && ![self.syncDaemon.lastCompletedSync isEqualToDate:syncLastCompleted]) {
200 [syncLastCompleted release];
201 syncLastCompleted = [self.syncDaemon.lastCompletedSync copy];
203 return syncLastCompleted;
206 - (PithosSyncDaemon *)syncDaemon {
207 @synchronized(self) {
208 if (self.syncActive && !syncDaemon)
209 syncDaemon = [[PithosSyncDaemon alloc] initWithDirectoryPath:self.syncDirectoryPath
211 containersDictionary:self.syncContainersDictionary
212 resetLocalState:resetSyncDaemonLocalState];
213 resetSyncDaemonLocalState = NO;
218 - (NSString *)serverURL {
219 if (![self urlIsValid:serverURL]) {
221 serverURL = [[NSString stringWithString:@"https://pithos.okeanos.grnet.gr"] retain];
226 - (void)setServerURL:(NSString *)aServerURL {
227 if (![self.serverURL isEqualToString:aServerURL] && [self urlIsValid:aServerURL]) {
229 serverURL = [aServerURL retain];
230 [storageURLPrefix release];
231 storageURLPrefix = nil;
234 [publicURLPrefix release];
235 publicURLPrefix = nil;
236 [loginURLPrefix release];
237 loginURLPrefix = nil;
239 @synchronized(self) {
241 resetSyncDaemonLocalState = YES;
242 [syncLastCompleted release];
243 syncLastCompleted = nil;
248 - (void)setAuthUser:(NSString *)anAuthUser {
249 if ([anAuthUser length] && ![anAuthUser isEqualToString:authUser]) {
251 authUser = [anAuthUser retain];
253 @synchronized(self) {
255 resetSyncDaemonLocalState = YES;
256 [syncLastCompleted release];
257 syncLastCompleted = nil;
263 - (void)setAuthToken:(NSString *)anAuthToken {
264 if ([anAuthToken length] && ![anAuthToken isEqualToString:authToken]) {
266 authToken = [anAuthToken retain];
268 @synchronized(self) {
274 - (NSString *)storageURLPrefix {
275 if (![self urlIsValid:storageURLPrefix]) {
276 [storageURLPrefix release];
278 storageURLPrefix = [[self.serverURL stringByAppendingFormat:@"/%@", versionResource] retain];
280 storageURLPrefix = [self.serverURL copy];
282 return storageURLPrefix;
285 - (void)setStorageURLPrefix:(NSString *)aStorageURLPrefix {
286 if (![self.storageURLPrefix isEqualToString:aStorageURLPrefix] && [self urlIsValid:aStorageURLPrefix]) {
287 [storageURLPrefix release];
288 storageURLPrefix = [aStorageURLPrefix retain];
292 - (NSString *)authURL {
293 if (![self urlIsValid:authURL]) {
296 authURL = [[self.serverURL stringByAppendingFormat:@"/%@", versionResource] retain];
298 authURL = [self.serverURL copy];
303 - (void)setAuthURL:(NSString *)anAuthURL {
304 if (![self.authURL isEqualToString:anAuthURL] && [self urlIsValid:anAuthURL]) {
306 authURL = [anAuthURL retain];
310 - (NSString *)publicURLPrefix {
311 if (![self urlIsValid:publicURLPrefix]) {
312 [publicURLPrefix release];
314 publicURLPrefix = [[self.serverURL stringByAppendingFormat:@"/%@", publicResource] retain];
316 publicURLPrefix = [self.serverURL copy];
318 return publicURLPrefix;
321 - (void)setPublicURLPrefix:(NSString *)aPublicURLPrefix {
322 if (![self.publicURLPrefix isEqualToString:aPublicURLPrefix] && [self urlIsValid:aPublicURLPrefix]) {
323 [publicURLPrefix release];
324 publicURLPrefix = [aPublicURLPrefix retain];
328 - (NSString *)loginURLPrefix {
329 if (![self urlIsValid:loginURLPrefix]) {
330 [loginURLPrefix release];
332 loginURLPrefix = [[self.serverURL stringByAppendingFormat:@"/%@", loginResource] retain];
334 loginURLPrefix = [self.serverURL copy];
336 return loginURLPrefix;
339 - (void)setLoginURLPrefix:(NSString *)aLoginURLPrefix {
340 if (![self.loginURLPrefix isEqualToString:aLoginURLPrefix] && [self urlIsValid:aLoginURLPrefix]) {
341 [loginURLPrefix release];
342 loginURLPrefix = [aLoginURLPrefix retain];
346 - (ASIPithos *)pithos {
347 @synchronized(self) {
348 if (!pithos || updatePithos) {
350 pithos = [[ASIPithos pithos] retain];
351 pithos.authUser = authUser;
352 pithos.authToken = authToken;
353 pithos.storageURLPrefix = self.storageURLPrefix;
354 pithos.authURL = self.authURL;
355 pithos.publicURLPrefix = self.publicURLPrefix;
357 if (accountNode && ![accountNode.pithos isEqualTo:pithos]) {
358 accountNode.pithos = pithos;
360 [accountNode refreshInfo];
369 - (PithosAccountNode *)accountNode {
371 accountNode = [[PithosAccountNode alloc] initWithPithos:self.pithos];
372 accountNode.childrenUpdatedNotificationName = nil;
373 accountNode.inheritChildrenUpdatedNotificationName = YES;
381 - (void)authenticateWithServerURL:(NSString *)aServerURL authUser:(NSString *)anAuthUser authToken:(NSString *)anAuthToken {
382 self.serverURL = aServerURL;
383 self.authUser = anAuthUser;
384 self.authToken = anAuthToken;
385 NSLog(@"Account: %@\nauthentication", self);
386 if (![authUser length] || ![authToken length]) {
388 self.syncActive = NO;
389 // XXX Show preferences with self as the selected account?
393 self.syncDaemon.pithos = self.pithos;
395 [self.syncDaemon startDaemon];
400 - (void)loginWithServerURL:(NSString *)aServerURL {
401 self.serverURL = aServerURL;
402 NSProcessInfo *processInfo = [NSProcessInfo processInfo];
403 NSString *loginURL = [NSString stringWithFormat:@"%@?next=pithos://%@_%d/%@",
404 self.loginURLPrefix, [processInfo processName], [processInfo processIdentifier], self.name];
405 NSLog(@"Account: %@\nloginURL: %@", self, loginURL);
406 [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:loginURL]];
409 - (void)updateSyncWithSyncActive:(BOOL)aSyncActive
410 syncDirectoryPath:(NSString *)aSyncDirectoryPath
411 syncContainersDictionary:(NSMutableDictionary *)aSyncContainersDictionary {
412 self.syncContainersDictionary = aSyncContainersDictionary;
413 self.syncDirectoryPath = aSyncDirectoryPath;
414 self.syncActive = aSyncActive;
416 self.syncDaemon.containersDictionary = self.syncContainersDictionary;
417 self.syncDaemon.directoryPath = self.syncDirectoryPath;
419 [self.syncDaemon startDaemon];
424 #pragma mark NSCoding
426 - (id)initWithCoder:(NSCoder *)decoder {
427 if ((self = [super init])) {
428 self.uniqueName = [decoder decodeObjectForKey:@"uniqueName"];
429 self.active = [decoder decodeBoolForKey:@"active"];
430 name = [[decoder decodeObjectForKey:@"name"] retain];
432 self.syncActive = [decoder decodeBoolForKey:@"syncActive"];
433 self.syncDirectoryPath = [decoder decodeObjectForKey:@"syncDirectoryPath"];
434 NSDictionary *immutableContainersDictionary = [decoder decodeObjectForKey:@"syncContainersDictionary"];
435 syncContainersDictionary = [[NSMutableDictionary alloc] initWithCapacity:[immutableContainersDictionary count]];
436 for (NSString *containerName in immutableContainersDictionary) {
437 [syncContainersDictionary setObject:[NSMutableArray arrayWithArray:[immutableContainersDictionary objectForKey:containerName]]
438 forKey:containerName];
440 self.syncLastCompleted = [decoder decodeObjectForKey:@"syncLastCompleted"];
442 self.serverURL = [decoder decodeObjectForKey:@"serverURL"];
443 self.versionResource = [decoder decodeObjectForKey:@"versionResource"];
444 self.loginResource = [decoder decodeObjectForKey:@"loginResource"];
445 self.publicResource = [decoder decodeObjectForKey:@"publicResource"];
447 self.authUser = [decoder decodeObjectForKey:@"authUser"];
448 self.authToken = [decoder decodeObjectForKey:@"authToken"];
449 self.storageURLPrefix = [decoder decodeObjectForKey:@"storageURLPrefix"];
450 self.authURL = [decoder decodeObjectForKey:@"authURL"];
451 self.publicURLPrefix = [decoder decodeObjectForKey:@"publicURLPrefix"];
452 self.loginURLPrefix = [decoder decodeObjectForKey:@"loginURLPrefix"];
454 if (![authUser length] || ![authToken length] || ![self.storageURLPrefix length])
457 resetSyncDaemonLocalState = NO;
462 - (void)encodeWithCoder:(NSCoder *)encoder {
463 [encoder encodeObject:uniqueName forKey:@"uniqueName"];
464 [encoder encodeBool:active forKey:@"active"];
465 [encoder encodeObject:name forKey:@"name"];
467 [encoder encodeBool:syncActive forKey:@"syncActive"];
468 [encoder encodeObject:syncDirectoryPath forKey:@"syncDirectoryPath"];
469 [encoder encodeObject:syncContainersDictionary forKey:@"syncContainersDictionary"];
470 [encoder encodeObject:self.syncLastCompleted forKey:@"syncLastCompleted"];
472 [encoder encodeObject:serverURL forKey:@"serverURL"];
473 [encoder encodeObject:versionResource forKey:@"versionResource"];
474 [encoder encodeObject:publicResource forKey:@"publicResource"];
475 [encoder encodeObject:loginResource forKey:@"loginResource"];
477 [encoder encodeObject:authUser forKey:@"authUser"];
478 [encoder encodeObject:authToken forKey:@"authToken"];
479 [encoder encodeObject:storageURLPrefix forKey:@"storageURLPrefix"];
480 [encoder encodeObject:authURL forKey:@"authURL"];
481 [encoder encodeObject:publicURLPrefix forKey:@"publicURLPrefix"];
482 [encoder encodeObject:loginURLPrefix forKey:@"loginURLPrefix"];