Reset sync daemon local state if the client version has changed
[pithos-macos] / pithos-macos / PithosAccount.m
index f97b23f..fe5ca8d 100644 (file)
 #import "PithosAccount.h"
 #import "PithosSyncDaemon.h"
 #import "ASIPithos.h"
+#import "ASIPithosRequest.h"
 #import "PithosAccountNode.h"
+#import "PithosSharingAccountsNode.h"
+#import "PithosUtilities.h"
 #import "pithos_macosAppDelegate.h"
 
 @interface PithosAccount (Internal)
 @end
 
 @implementation PithosAccount
-@synthesize uniqueName, active, name;
-@synthesize syncActive, syncDirectoryPath, syncContainerName, syncLastCompleted, syncDaemon;
-@synthesize serverURL, versionResource, loginResource, publicResource;
-@synthesize authUser, authToken, storageURLPrefix, authURL, loginURLPrefix, publicURLPrefix;
-@synthesize pithos, accountNode;
+@synthesize uniqueName, active, name, clientVersion;
+@synthesize syncActive, syncDirectoryPath, syncAccountsDictionary, syncSkipHidden, syncLastCompleted, syncDaemon;
+@synthesize serverURL, versionResource, loginResource, publicResource, userCatalogResource;
+@synthesize authUser, authToken, storageURLPrefix, authURL, loginURLPrefix, publicURLPrefix, userCatalogURL, userCatalog;
+@synthesize pithos, accountNode, sharingAccountsNode;
 
 #pragma mark -
 #pragma mark Object Lifecycle
 
 + (id)pithosAccount {
-    PithosAccount *pithosAccount = [[[self alloc] init] autorelease];
+    PithosAccount *pithosAccount = [[self alloc] init];
     pithosAccount.uniqueName = [NSString stringWithFormat:@"pithosAccount-%f", [NSDate timeIntervalSinceReferenceDate]];
-    pithosAccount.versionResource = [NSString stringWithString:@"v1"];
-    pithosAccount.loginResource = [NSString stringWithString:@"login"];
+    pithosAccount.clientVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
+    pithosAccount.syncSkipHidden = YES;
     return pithosAccount;
 }
 
-- (void)dealloc {
-    [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];
-    [syncContainerName release];
-    [syncDirectoryPath release];
-    [name release];
-    [uniqueName release];
-    [super dealloc];
-}
 
 - (NSString *)description {
-    return [NSString stringWithFormat:@"uniqueName: %@, active: %d, name: %@, syncActive: %d, syncDirectoryPath: %@, syncContainerName: %@, syncLastCompleted: %@, serverURL: %@, versionResource: %@, loginResource: %@, publicResource: %@, authUser: %@, authToken: %@, storageURLPrefix: %@, authURL: %@, loginURLPrefix: %@, publicURLPrefix: %@", 
-            uniqueName, active, name, syncActive, syncDirectoryPath, syncContainerName, syncLastCompleted, serverURL, versionResource, loginResource, publicResource, authUser, authToken, storageURLPrefix, authURL, loginURLPrefix, publicURLPrefix];
+    return [NSString stringWithFormat:@"uniqueName: %@, active: %d, name: %@, clientVersion: %@, syncActive: %d, syncDirectoryPath: %@, syncAccountsDictionary: %@, syncSkipHidden: %d, syncLastCompleted: %@, serverURL: %@, versionResource: %@, loginResource: %@, publicResource: %@, userCatalogResource: %@, authUser: %@, authToken: %@, storageURLPrefix: %@, authURL: %@, loginURLPrefix: %@, publicURLPrefix: %@, userCatalogResource: %@",
+            uniqueName, active, name, clientVersion, syncActive, syncDirectoryPath, syncAccountsDictionary, syncSkipHidden, syncLastCompleted, serverURL, versionResource, loginResource, publicResource, userCatalogResource, authUser, authToken, storageURLPrefix, authURL, loginURLPrefix, publicURLPrefix, userCatalogResource];
 }
 
 #pragma mark -
 
 - (NSString *)name {
     if (![name length]) {
-        [name release];
         NSDictionary *pithosAccountsDictionary = [(pithos_macosAppDelegate *)[[NSApplication sharedApplication] delegate] pithosAccountsDictionary];
-        NSString *namePrefix = [NSString stringWithString:@"okeanos"];
+        NSString *namePrefix = @"okeanos";
         NSUInteger nameSuffix = 1;
-        name = [NSString stringWithString:@"okeanos"];
+        name = @"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 = [NSString stringWithFormat:@"%@%ld", namePrefix, ++nameSuffix];
         }
-        [name retain];
     }
     return name;
 }
     if (![self.name isEqualToString:aName] && [aName length] && ![pithosAccountsDictionary objectForKey:aName]) {
         [pithosAccountsDictionary setObject:self forKey:aName];
         [pithosAccountsDictionary removeObjectForKey:name];
-        [name release];
-        name = [aName retain];
+        name = aName;
     }
 }
 
 
 - (NSString *)syncDirectoryPath {
     if (![syncDirectoryPath length]) {
-        [syncDirectoryPath release];
-        syncDirectoryPath = [[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] 
-                              stringByAppendingPathComponent:self.name] retain];
+        syncDirectoryPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] 
+                              stringByAppendingPathComponent:self.name];
     }
     return syncDirectoryPath;
 }
     if (![self.syncDirectoryPath isEqualToString:aSyncDirectoryPath] && [aSyncDirectoryPath length]) {
         BOOL isDirectory;
         if (![[NSFileManager defaultManager] fileExistsAtPath:aSyncDirectoryPath isDirectory:&isDirectory] || isDirectory) {
-            [syncDirectoryPath release];
-            syncDirectoryPath = [aSyncDirectoryPath retain];
+            syncDirectoryPath = aSyncDirectoryPath;
         } else {
             return;
         }
 
         @synchronized(self) {
             resetSyncDaemonLocalState = YES;
-            [syncLastCompleted release];
             syncLastCompleted = nil;
         }
     }
 }
 
-- (NSString *)syncContainerName {
-    if (![syncContainerName length]) {
-        [syncContainerName release];
-        syncContainerName = [[NSString stringWithString:@"pithos"] retain];
+- (NSMutableDictionary *)syncAccountsDictionary {
+    if (!syncAccountsDictionary) {
+        syncAccountsDictionary = [NSMutableDictionary dictionaryWithObject:[NSMutableDictionary dictionaryWithObject:[NSMutableSet set] 
+                                                                                                              forKey:@"pithos"]
+                                                                    forKey:@""];
     }        
-    return syncContainerName;
-}
-
-- (void)setSyncContainerName:(NSString *)aSyncContainerName {
-    if (![self.syncContainerName isEqualToString:aSyncContainerName] && [aSyncContainerName length]) {
-        [syncContainerName release];
-        syncContainerName = [aSyncContainerName retain];
+    return syncAccountsDictionary;
+}
+
+- (void)setSyncAccountsDictionary:(NSMutableDictionary *)aSyncAccountsDictionary {
+    if (aSyncAccountsDictionary && ![self.syncAccountsDictionary isEqualToDictionary:aSyncAccountsDictionary]) {
+        syncAccountsDictionary = [[NSMutableDictionary alloc] initWithCapacity:[aSyncAccountsDictionary count]];
+        for (NSString *accountName in aSyncAccountsDictionary) {
+            NSDictionary *aSyncContainersDictionary = [aSyncAccountsDictionary objectForKey:accountName];
+            NSMutableDictionary *syncContainersDictionary = [NSMutableDictionary dictionary];
+            for (NSString *containerName in aSyncContainersDictionary) {
+                if (![accountName isEqualToString:@""] || ![[containerName lowercaseString] isEqualToString:@"shared with me"])
+                    [syncContainersDictionary setObject:[NSMutableSet setWithSet:[aSyncContainersDictionary objectForKey:containerName]] 
+                                                 forKey:containerName];
+            }
+            if ([syncContainersDictionary count])
+                [syncAccountsDictionary setObject:syncContainersDictionary forKey:accountName];
+        }
         
         @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;
         if (self.syncActive && !syncDaemon)
             syncDaemon = [[PithosSyncDaemon alloc] initWithDirectoryPath:self.syncDirectoryPath 
                                                            pithosAccount:self 
-                                                           containerName:self.syncContainerName 
+                                                      accountsDictionary:self.syncAccountsDictionary 
+                                                              skipHidden:self.syncSkipHidden 
                                                          resetLocalState:resetSyncDaemonLocalState];
         resetSyncDaemonLocalState = NO;
     }
 
 - (NSString *)serverURL {
     if (![self urlIsValid:serverURL]) {
-        [serverURL release];
-        serverURL = [[NSString stringWithString:@"https://plus.pithos.grnet.gr"] retain];
+        serverURL = @"https://pithos.okeanos.grnet.gr";
     }
     return serverURL;
 }
 
 - (void)setServerURL:(NSString *)aServerURL {
     if (![self.serverURL isEqualToString:aServerURL] && [self urlIsValid:aServerURL]) {
-        [serverURL release];
-        serverURL = [aServerURL retain];
-        [storageURLPrefix release];
+        serverURL = aServerURL;
         storageURLPrefix = nil;
-        [authURL release];
         authURL = nil;
-        [publicURLPrefix release];
         publicURLPrefix = nil;
-        [loginURLPrefix release];
         loginURLPrefix = nil;
 
         @synchronized(self) {
             updatePithos = YES;
             resetSyncDaemonLocalState = YES;
-            [syncLastCompleted release];
             syncLastCompleted = nil;
         }
     }
 }
 
+- (NSString *)versionResource {
+    if (!versionResource) {
+        versionResource = @"v1";
+    }
+    return versionResource;
+}
+
+- (NSString *)loginResource {
+    if (!loginResource) {
+        loginResource = @"login";
+    }
+    return loginResource;
+}
+
+- (NSString *)userCatalogResource {
+    if (!userCatalogResource) {
+        userCatalogResource = @"user_catalogs";
+    }
+    return userCatalogResource;
+}
+
 - (void)setAuthUser:(NSString *)anAuthUser {
     if ([anAuthUser length] && ![anAuthUser isEqualToString:authUser]) {
-        [authUser release];
-        authUser = [anAuthUser retain];
+        authUser = anAuthUser;
         
         @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];
+        authToken = anAuthToken;
         
         @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];
+        storageURLPrefix = [self.serverURL stringByAppendingFormat:@"/%@", self.versionResource];
     }
     return storageURLPrefix;
 }
 
 - (void)setStorageURLPrefix:(NSString *)aStorageURLPrefix {
     if (![self.storageURLPrefix isEqualToString:aStorageURLPrefix] && [self urlIsValid:aStorageURLPrefix]) {
-        [storageURLPrefix release];
-        storageURLPrefix = [aStorageURLPrefix retain];
+        storageURLPrefix = aStorageURLPrefix;
     }
 }
 
 - (NSString *)authURL {
     if (![self urlIsValid:authURL]) {
-        [authURL release];
-        if (versionResource)
-            authURL = [[self.serverURL stringByAppendingFormat:@"/%@", versionResource] retain];
-        else
-            authURL = [self.serverURL copy];
+        authURL = [self.serverURL stringByAppendingFormat:@"/%@", self.versionResource];
     }
     return authURL;
 }
 
 - (void)setAuthURL:(NSString *)anAuthURL {
     if (![self.authURL isEqualToString:anAuthURL] && [self urlIsValid:anAuthURL]) {
-        [authURL release];
-        authURL = [anAuthURL retain];
+        authURL = anAuthURL;
     }
 }
 
 - (NSString *)publicURLPrefix {
     if (![self urlIsValid:publicURLPrefix]) {
-        [publicURLPrefix release];
         if (publicResource)
-            publicURLPrefix = [[self.serverURL stringByAppendingFormat:@"/%@", publicResource] retain];
+            publicURLPrefix = [self.serverURL stringByAppendingFormat:@"/%@", publicResource];
         else
             publicURLPrefix = [self.serverURL copy];
     }
 
 - (void)setPublicURLPrefix:(NSString *)aPublicURLPrefix {
     if (![self.publicURLPrefix isEqualToString:aPublicURLPrefix] && [self urlIsValid:aPublicURLPrefix]) {
-        [publicURLPrefix release];
-        publicURLPrefix = [aPublicURLPrefix retain];
+        publicURLPrefix = aPublicURLPrefix;
     }
 }
 
 - (NSString *)loginURLPrefix {
     if (![self urlIsValid:loginURLPrefix]) {
-        [loginURLPrefix release];
-        if (loginResource)
-            loginURLPrefix = [[self.serverURL stringByAppendingFormat:@"/%@", loginResource] retain];
-        else
-            loginURLPrefix = [self.serverURL copy];
+        loginURLPrefix = [self.serverURL stringByAppendingFormat:@"/%@", self.loginResource];
     }
     return loginURLPrefix;
 }
 
 - (void)setLoginURLPrefix:(NSString *)aLoginURLPrefix {
     if (![self.loginURLPrefix isEqualToString:aLoginURLPrefix] && [self urlIsValid:aLoginURLPrefix]) {
-        [loginURLPrefix release];
-        loginURLPrefix = [aLoginURLPrefix retain];
+        loginURLPrefix = aLoginURLPrefix;
+    }
+}
+
+- (NSString *)userCatalogURL {
+    if (![self urlIsValid:userCatalogURL]) {
+        userCatalogURL = [self.serverURL stringByAppendingFormat:@"/%@", self.userCatalogResource];
+    }
+    return userCatalogURL;
+}
+
+- (void)setUserCatalogURL:(NSString *)aUserCatalogURL {
+    if (![self.userCatalogURL isEqualToString:aUserCatalogURL] && [self urlIsValid:aUserCatalogURL]) {
+        userCatalogURL = aUserCatalogURL;
+    }
+}
+
+- (NSMutableDictionary *)userCatalog {
+    if (!userCatalog) {
+        userCatalog = [NSMutableDictionary dictionary];
     }
+    return userCatalog;
 }
 
 - (ASIPithos *)pithos {
     @synchronized(self) {
         if (!pithos || updatePithos) {
-            [pithos release];
-            pithos = [[ASIPithos pithos] retain];
+            pithos = [ASIPithos pithos];
             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];
-            }
-            
+            pithos.userCatalogURL = self.userCatalogURL;
             updatePithos = NO;
         }
     }
 - (PithosAccountNode *)accountNode {
     if (!accountNode) {
         accountNode = [[PithosAccountNode alloc] initWithPithos:self.pithos];
+        accountNode.childrenUpdatedNotificationName = nil;
+        accountNode.inheritChildrenUpdatedNotificationName = YES;
+        accountNode.pithosAccountManager = self;
     }
     return accountNode;
 }
 
+- (PithosSharingAccountsNode *)sharingAccountsNode {
+    if (!sharingAccountsNode) {
+        sharingAccountsNode = [[PithosSharingAccountsNode alloc] initWithPithos:self.pithos];
+        sharingAccountsNode.childrenUpdatedNotificationName = nil;
+        sharingAccountsNode.inheritChildrenUpdatedNotificationName = YES;
+        sharingAccountsNode.pithosAccountManager = self;
+    }
+    return sharingAccountsNode;
+}
+
 #pragma mark -
 #pragma mark Actions
 
     self.serverURL = aServerURL;
     self.authUser = anAuthUser;
     self.authToken = anAuthToken;
-    NSLog(@"Account: %@\nauthentication", self);
+    DLog(@"Account: %@\nauthentication", self);
     if (![authUser length] || ![authToken length]) {
         self.active = NO;
         self.syncActive = NO;
             if (self.syncActive)
                 [self.syncDaemon startDaemon];
         }
+        if (accountNode)
+            self.accountNode.pithos = self.pithos;
+        if (sharingAccountsNode)
+            self.sharingAccountsNode.pithos = self.pithos;
+        if (accountNode) {
+            if (self.accountNode.children) {
+            }
+            [self.accountNode refreshInfo];
+        }
+        if (sharingAccountsNode && self.sharingAccountsNode.children) {
+        }
+        
+        [self updateUserCatalogForForDisplaynames:nil UUIDs:[NSArray arrayWithObject:authUser]];
     }
 }
 
 - (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);
+    NSString *loginURL = [NSString stringWithFormat:@"%@?next=pithos://%d/%@&force=", 
+                          self.loginURLPrefix, [processInfo processIdentifier], [ASIPithosRequest encodeToPercentEscape:self.name]];
+    DLog(@"Account: %@\nloginURL: %@", self, loginURL);
     [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:loginURL]];
 }
 
-- (void)updateSyncWithSyncActive:(BOOL)aSyncActive syncDirectoryPath:(NSString *)aSyncDirectoryPath {
+- (void)updateSyncWithSyncActive:(BOOL)aSyncActive 
+               syncDirectoryPath:(NSString *)aSyncDirectoryPath 
+          syncAccountsDictionary:(NSMutableDictionary *)aSyncAccountsDictionary 
+                  syncSkipHidden:(BOOL)aSyncSkipHidden {
+    self.syncAccountsDictionary = aSyncAccountsDictionary;
     self.syncDirectoryPath = aSyncDirectoryPath;
+    self.syncSkipHidden = aSyncSkipHidden;
     self.syncActive = aSyncActive;
     if (syncDaemon) {
+        self.syncDaemon.accountsDictionary = self.syncAccountsDictionary;
         self.syncDaemon.directoryPath = self.syncDirectoryPath;
+        self.syncDaemon.skipHidden = self.syncSkipHidden;
         if (self.syncActive)
             [self.syncDaemon startDaemon];
     }    
 }
 
+- (ASIPithosRequest *)updateUserCatalogForForDisplaynames:(NSArray *)displaynames UUIDs:(NSArray *)UUIDs {
+    ASIPithosRequest *userCatalogRequest = [ASIPithosRequest userCatalogRequestWithPithos:self.pithos
+                                                                             displaynames:displaynames
+                                                                                    UUIDs:UUIDs];
+    [PithosUtilities startAndWaitForRequest:userCatalogRequest];
+    if (userCatalogRequest.error || ((userCatalogRequest.responseStatusCode != 200) && (userCatalogRequest.responseStatusCode != 404))) {
+        // Don't show alert on 404, since it can be a pre-UUID server.
+        [PithosUtilities httpRequestErrorAlertWithRequest:userCatalogRequest];
+    } else if (userCatalogRequest.responseStatusCode == 200) {
+        NSDictionary *catalogs = [userCatalogRequest catalogs];
+        NSDictionary *displaynameCatalog = [catalogs objectForKey:@"displayname_catalog"];
+        for (NSString *displayname in displaynameCatalog) {
+            [self.userCatalog setObject:displayname forKey:[displaynameCatalog objectForKey:displayname]];
+        }
+        if (UUIDs) {
+            NSDictionary *UUIDCatalog = [catalogs objectForKey:@"uuid_catalog"];
+            for (NSString *UUID in UUIDs) {
+                NSString *displayname = [UUIDCatalog objectForKey:UUID];
+                if (displayname) {
+                    [self.userCatalog setObject:displayname forKey:UUID];
+                } else {
+                    [self.userCatalog removeObjectForKey:UUID];
+                }
+            }
+        }
+    }
+    return userCatalogRequest;
+}
+
+- (NSString *)displaynameForUUID:(NSString *)UUID safe:(BOOL)safe {
+    NSString *displayName = [userCatalog objectForKey:UUID];
+    if (safe && !displayName) {
+        return UUID;
+    } else {
+        return displayName;
+    }
+}
+
+- (NSString *)displaynameForUUID:(NSString *)UUID {
+    return [self displaynameForUUID:UUID safe:NO];
+}
+
 #pragma mark -
 #pragma mark NSCoding
 
     if ((self = [super init])) {
         self.uniqueName = [decoder decodeObjectForKey:@"uniqueName"];
         self.active = [decoder decodeBoolForKey:@"active"];
-        name = [[decoder decodeObjectForKey:@"name"] retain];
+        name = [decoder decodeObjectForKey:@"name"];
+        self.clientVersion = [decoder decodeObjectForKey:@"clientVersion"];
 
         self.syncActive = [decoder decodeBoolForKey:@"syncActive"];
         self.syncDirectoryPath = [decoder decodeObjectForKey:@"syncDirectoryPath"];
-        self.syncContainerName = [decoder decodeObjectForKey:@"syncContainerName"];
+        self.syncAccountsDictionary = [decoder decodeObjectForKey:@"syncAccountsDictionary"];
+        self.syncSkipHidden = [decoder decodeBoolForKey:@"syncSkipHidden"];
         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.userCatalogResource = [decoder decodeObjectForKey:@"userCatalogResource"];
         
         self.authUser = [decoder decodeObjectForKey:@"authUser"];
         self.authToken = [decoder decodeObjectForKey:@"authToken"];
         self.authURL = [decoder decodeObjectForKey:@"authURL"];
         self.publicURLPrefix = [decoder decodeObjectForKey:@"publicURLPrefix"];
         self.loginURLPrefix = [decoder decodeObjectForKey:@"loginURLPrefix"];
+        self.userCatalogURL = [decoder decodeObjectForKey:@"userCatalogURL"];
+        self.userCatalog = [decoder decodeObjectForKey:@"userCatalog"];
         
         if (![authUser length] || ![authToken length] || ![self.storageURLPrefix length])
             self.active = NO;
         
-        resetSyncDaemonLocalState = NO;
+        NSString *currentVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
+        if (!clientVersion || ![clientVersion isEqualToString:currentVersion]) {
+            resetSyncDaemonLocalState = YES;
+            self.clientVersion = currentVersion;
+        } else {
+            resetSyncDaemonLocalState = NO;
+        }
     }
     return self;
 }
     [encoder encodeObject:uniqueName forKey:@"uniqueName"];
     [encoder encodeBool:active forKey:@"active"];
     [encoder encodeObject:name forKey:@"name"];
+    [encoder encodeObject:clientVersion forKey:@"clientVersion"];
     
     [encoder encodeBool:syncActive forKey:@"syncActive"];
     [encoder encodeObject:syncDirectoryPath forKey:@"syncDirectoryPath"];
-    [encoder encodeObject:syncContainerName forKey:@"syncContainerName"];
+    [encoder encodeObject:syncAccountsDictionary forKey:@"syncAccountsDictionary"];
+    [encoder encodeBool:syncSkipHidden forKey:@"syncSkipHidden"];
     [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:loginResource forKey:@"userCatalogResource"];
     
     [encoder encodeObject:authUser forKey:@"authUser"];
     [encoder encodeObject:authToken forKey:@"authToken"];
     [encoder encodeObject:authURL forKey:@"authURL"];
     [encoder encodeObject:publicURLPrefix forKey:@"publicURLPrefix"];
     [encoder encodeObject:loginURLPrefix forKey:@"loginURLPrefix"];
+    [encoder encodeObject:userCatalogURL forKey:@"userCatalogURL"];
+    [encoder encodeObject:userCatalog forKey:@"userCatalog"];
 }
 
 @end