Using MD5 to quickly check for local modifications before calculating the expensive...
[pithos-ms-client] / trunk / Pithos.Core / Agents / StatusAgent.cs
index 299b41f..b95645d 100644 (file)
@@ -107,9 +107,11 @@ namespace Pithos.Core.Agents
 
         private static void MigrateOldDb(string dbPath, string appDataPath)
         {
-            Contract.Requires(!String.IsNullOrWhiteSpace(dbPath));
-            Contract.Requires(!String.IsNullOrWhiteSpace(appDataPath));
-
+            if(String.IsNullOrWhiteSpace(dbPath))
+                throw new ArgumentNullException("dbPath");
+            if(String.IsNullOrWhiteSpace(appDataPath))
+                throw new ArgumentNullException("appDataPath");
+            Contract.EndContractBlock();
 
             var oldDbPath = Path.Combine(appDataPath, "Pithos", "pithos.db");
             var oldDbInfo = new FileInfo(oldDbPath);
@@ -344,6 +346,12 @@ namespace Pithos.Core.Agents
                         command.Parameters.AddWithValue("path", path);
                         
                         var affected = command.ExecuteNonQuery();
+                        if (affected == 0)
+                        {
+                            var createdState = FileState.CreateFor(FileInfoExtensions.FromPath(path));
+                            createdState.FileStatus = status;
+                            createdState.Create();
+                        }
                         return affected;
                     }
                 }
@@ -355,7 +363,7 @@ namespace Pithos.Core.Agents
             }
         }
 
-        private int UpdateStatusDirect(string absolutePath, FileStatus fileStatus, FileOverlayStatus overlayStatus)
+        private int UpdateStatusDirect(string absolutePath, FileStatus fileStatus, FileOverlayStatus overlayStatus, string conflictReason)
         {
             using (log4net.ThreadContext.Stacks["StatusAgent"].Push("UpdateStatusDirect"))
             {
@@ -368,15 +376,24 @@ namespace Pithos.Core.Agents
                     using (
                         var command =
                             new SQLiteCommand(
-                                "update FileState set OverlayStatus= :overlayStatus, FileStatus= :fileStatus where FilePath = :path COLLATE NOCASE ",
+                                "update FileState set OverlayStatus= :overlayStatus, FileStatus= :fileStatus,ConflictReason= :conflictReason where FilePath = :path COLLATE NOCASE ",
                                 connection))
                     {
 
                         command.Parameters.AddWithValue("path", absolutePath);
                         command.Parameters.AddWithValue("fileStatus", fileStatus);
                         command.Parameters.AddWithValue("overlayStatus", overlayStatus);
+                        command.Parameters.AddWithValue("conflictReason", conflictReason);
                         
                         var affected = command.ExecuteNonQuery();
+                        if (affected == 0)
+                        {
+                            var createdState=FileState.CreateFor(FileInfoExtensions.FromPath(absolutePath));
+                            createdState.FileStatus = fileStatus;
+                            createdState.OverlayStatus = overlayStatus;
+                            createdState.ConflictReason = conflictReason;
+                            createdState.Create();  
+                        }
                         return affected;
                     }
                 }
@@ -567,7 +584,7 @@ namespace Pithos.Core.Agents
             _persistenceAgent.Post(() =>FileState.RenameState(oldPath, newPath));
         }*/
 
-        public void SetFileState(string path, FileStatus fileStatus, FileOverlayStatus overlayStatus)
+        public void SetFileState(string path, FileStatus fileStatus, FileOverlayStatus overlayStatus, string conflictReason)
         {
             if (String.IsNullOrWhiteSpace(path))
                 throw new ArgumentNullException("path");
@@ -578,7 +595,7 @@ namespace Pithos.Core.Agents
             Debug.Assert(!path.Contains(FolderConstants.CacheFolder));
             Debug.Assert(!path.EndsWith(".ignore"));
 
-            _persistenceAgent.Post(() => UpdateStatusDirect(path, fileStatus, overlayStatus));
+            _persistenceAgent.Post(() => UpdateStatusDirect(path, fileStatus, overlayStatus, conflictReason));
         }
 
 /*
@@ -650,19 +667,18 @@ namespace Pithos.Core.Agents
                     else
                     {
                         command.CommandText =
-                            "INSERT INTO FileState (Id,FilePath,Checksum,Version,VersionTimeStamp,ShortHash,FileStatus,OverlayStatus) VALUES (:id,:path,:checksum,:version,:versionTimeStamp,:shortHash,:fileStatus,:overlayStatus)";
+                            "INSERT INTO FileState (Id,FilePath,Checksum,Version,VersionTimeStamp,ShortHash,FileStatus,OverlayStatus,ObjectID) VALUES (:id,:path,:checksum,:version,:versionTimeStamp,:shortHash,:fileStatus,:overlayStatus,:objectID)";
                         command.Parameters.AddWithValue("id", Guid.NewGuid());
                     }
 
                     command.Parameters.AddWithValue("path", path);
-                    command.Parameters.AddWithValue("checksum", objectInfo.Hash);
-                    command.Parameters.AddWithValue("shortHash", "");
+                    command.Parameters.AddWithValue("checksum", objectInfo.X_Object_Hash);
+                    command.Parameters.AddWithValue("shortHash", objectInfo.ETag);
                     command.Parameters.AddWithValue("version", objectInfo.Version);
-                    command.Parameters.AddWithValue("versionTimeStamp",
-                                                    objectInfo.VersionTimestamp);
+                    command.Parameters.AddWithValue("versionTimeStamp", objectInfo.VersionTimestamp);
                     command.Parameters.AddWithValue("fileStatus", FileStatus.Unchanged);
-                    command.Parameters.AddWithValue("overlayStatus",
-                                                    FileOverlayStatus.Normal);
+                    command.Parameters.AddWithValue("overlayStatus", FileOverlayStatus.Normal);
+                    command.Parameters.AddWithValue("objectID",objectInfo.UUID);
 
                     var affected = command.ExecuteNonQuery();
                     return;
@@ -744,7 +760,7 @@ namespace Pithos.Core.Agents
             if (!Path.IsPathRooted(path))
                 throw new ArgumentException("The path must be rooted", "path");
             Contract.EndContractBlock();
-
+            //TODO: May throw if the agent is cleared for some reason. Should never happen
             _persistenceAgent.Post(() => DeleteFolderDirect(path));   
         }
 
@@ -842,6 +858,65 @@ namespace Pithos.Core.Agents
             _persistenceAgent.Post(() => FileState.UpdateChecksum(path, shortHash,checksum));
         }
 
+
+        public void CleanupOrphanStates()
+        {
+            //Orphan states are those that do not correspond to an account, ie. their paths
+            //do not start with the root path of any registered account
+
+            var roots=(from account in Settings.Accounts
+                      select account.RootPath).ToList();
+            
+            var allStates = from state in FileState.Queryable
+                select state.FilePath;
+
+            foreach (var statePath in allStates)
+            {
+                if (!roots.Any(root=>statePath.StartsWith(root,StringComparison.InvariantCultureIgnoreCase)))
+                    this.DeleteDirect(statePath);
+            }
+        }
+
+        public void CleanupStaleStates(AccountInfo accountInfo, List<ObjectInfo> objectInfos)
+        {
+            if (accountInfo == null)
+                throw new ArgumentNullException("accountInfo");
+            if (objectInfos == null)
+                throw new ArgumentNullException("objectInfos");
+            Contract.EndContractBlock();
+            
+
+
+            //Stale states are those that have no corresponding local or server file
+            
+
+            var agent=FileAgent.GetFileAgent(accountInfo);
+
+            var localFiles=agent.EnumerateFiles();
+            var localSet = new HashSet<string>(localFiles);
+
+            //RelativeUrlToFilePath will fail for
+            //infos of accounts, containers which have no Name
+
+            var serverFiles = from info in objectInfos
+                              where info.Name != null
+                              select Path.Combine(accountInfo.AccountPath,info.RelativeUrlToFilePath(accountInfo.UserName));
+            var serverSet = new HashSet<string>(serverFiles);
+
+            var allStates = from state in FileState.Queryable
+                            where state.FilePath.StartsWith(agent.RootPath)
+                            select state.FilePath;
+            var stateSet = new HashSet<string>(allStates);
+            stateSet.ExceptWith(serverSet);
+            stateSet.ExceptWith(localSet);
+
+            foreach (var remainder in stateSet)
+            {
+                DeleteDirect(remainder);
+            }
+
+            
+        }
     }