Changed ETag calculation to SHA256
[pithos-ms-client] / trunk / Pithos.Core / Agents / FileAgent.cs
index 7a45474..e1db414 100644 (file)
-#region
-/* -----------------------------------------------------------------------
- * <copyright file="FileAgent.cs" company="GRNet">
- * 
- * Copyright 2011-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.
- * </copyright>
- * -----------------------------------------------------------------------
- */
-#endregion
-using System;
-using System.Collections.Generic;
-using System.Diagnostics.Contracts;
-using System.IO;
-using System.Linq;
-using System.Reflection;
-using System.Threading.Tasks;
-using Pithos.Interfaces;
-using Pithos.Network;
-using log4net;
-
-namespace Pithos.Core.Agents
-{
-//    [Export]
-    public class FileAgent
-    {
-        private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
-
-        /*
-                Agent<WorkflowState> _agent;
-        */
-        private FileSystemWatcher _watcher;
-        private FileSystemWatcherAdapter _adapter;
-        private FileEventIdleBatch _eventIdleBatch;
-
-        //[Import]
-        public IStatusKeeper StatusKeeper { get; set; }
-
-        public IStatusNotification StatusNotification { get; set; }
-        //[Import]
-        public IPithosWorkflow Workflow { get; set; }
-        //[Import]
-        //public WorkflowAgent WorkflowAgent { get; set; }
-
-        private AccountInfo AccountInfo { get; set; }
-
-        internal string RootPath { get;  set; }
-        
-        public TimeSpan IdleTimeout { get; set; }
-
-        public PollAgent PollAgent { get; set; }
-
-        private void ProcessBatchedEvents(FileSystemEventArgs[] fileEvents)
-        {
-            var paths = new HashSet<string>();
-
-            foreach (var evt in fileEvents)
-            {
-                paths.Add(evt.FullPath);
-                if (evt is MovedEventArgs)
-                {
-                    paths.Add((evt as MovedEventArgs).OldFullPath);
-                }
-                else if (evt is RenamedEventArgs)
-                {
-                    paths.Add((evt as RenamedEventArgs).OldFullPath);
-                }
-            }
-                  
-            PollAgent.SynchNow(paths);
-        }
-
-/*
-        private void ProcessBatchedEvents(Dictionary<string, FileSystemEventArgs[]> fileEvents)
-        {
-            StatusNotification.SetPithosStatus(PithosStatus.LocalSyncing,String.Format("Uploading {0} files",fileEvents.Count));
-            //Start with events that do not originate in one of the ignored folders
-            var initialEvents = from evt in fileEvents
-                              where !IgnorePaths(evt.Key)
-                              select evt;
-
-            IEnumerable<KeyValuePair<string, FileSystemEventArgs[]>> cleanEvents;
-
-            
-            var selectiveEnabled = Selectives.IsSelectiveEnabled(AccountInfo.AccountKey);
-            //When selective sync is enabled,
-            if (selectiveEnabled)
-            {
-                //Include all selected items
-                var selectedEvents = from evt in initialEvents
-                                     where Selectives.IsSelected(AccountInfo, evt.Key)
-                                     select evt;                
-                //And all folder creations in the unselected folders
-                var folderCreations = from evt in initialEvents
-                                      let folderPath=evt.Key
-                                      //The original folder may not exist due to renames. Just make sure that the path is not a file
-                                      where !File.Exists(folderPath)
-                                            //We only want unselected items
-                                            && !Selectives.IsSelected(AccountInfo, folderPath)
-                                            //Is there any creation event related to the folder?
-                                            && evt.Value.Any(arg => arg.ChangeType == WatcherChangeTypes.Created)
-                                      select evt;
-                cleanEvents = selectedEvents.Union(folderCreations).ToList();
-            }
-            //If selective is disabled, only exclude the shared folders 
-            else
-            {
-                cleanEvents = (from evt in initialEvents
-                              where !evt.Key.IsSharedTo(AccountInfo)
-                              select evt).ToList();
-            }
-
-
-            foreach (var fileEvent in cleanEvents)
-            {
-                //var filePath = fileEvent.Key;
-                var changes = fileEvent.Value;
-
-                var isNotFile = !File.Exists(fileEvent.Key);
-                foreach (var change in changes)
-                {
-                    if (change.ChangeType == WatcherChangeTypes.Renamed)
-                    {
-                        var rename = (MovedEventArgs) change;
-                        _agent.Post(new WorkflowState(change)
-                                        {
-                                            AccountInfo = AccountInfo,
-                                            OldPath = rename.OldFullPath,
-                                            OldFileName = Path.GetFileName(rename.OldName),
-                                            Path = rename.FullPath,
-                                            FileName = Path.GetFileName(rename.Name),
-                                            TriggeringChange = rename.ChangeType
-                                        });
-                    }
-                    else
-                    {
-                        var isCreation = selectiveEnabled && isNotFile && change.ChangeType == WatcherChangeTypes.Created;
-                        _agent.Post(new WorkflowState(change)
-                                        {
-                                            AccountInfo = AccountInfo,
-                                            Path = change.FullPath,
-                                            FileName = Path.GetFileName(change.Name),
-                                            TriggeringChange = change.ChangeType,
-                                            IsCreation=isCreation
-                                        });
-                    }
-                }
-            }
-            StatusNotification.SetPithosStatus(PithosStatus.LocalComplete);
-        }
-*/
-
-        public void Start(AccountInfo accountInfo,string rootPath)
-        {
-            if (accountInfo==null)
-                throw new ArgumentNullException("accountInfo");
-            if (String.IsNullOrWhiteSpace(rootPath))
-                throw new ArgumentNullException("rootPath");
-            if (!Path.IsPathRooted(rootPath))
-                throw new ArgumentException("rootPath must be an absolute path","rootPath");
-            if (IdleTimeout == null)
-                throw new InvalidOperationException("IdleTimeout must have a valid value");
-                Contract.EndContractBlock();
-
-            AccountInfo = accountInfo;
-            RootPath = rootPath;
-            
-            _eventIdleBatch = new FileEventIdleBatch(PollAgent,(int)IdleTimeout.TotalMilliseconds, ProcessBatchedEvents);
-
-            _watcher = new FileSystemWatcher(rootPath) { IncludeSubdirectories = true, InternalBufferSize = 8 * 4096 };
-            _adapter = new FileSystemWatcherAdapter(_watcher,this);
-
-            _adapter.Changed += OnFileEvent;
-            _adapter.Created += OnFileEvent;
-            _adapter.Deleted += OnFileEvent;
-            //_adapter.Renamed += OnRenameEvent;
-            _adapter.Moved += OnMoveEvent;
-            _watcher.EnableRaisingEvents = true;
-
-/*            
-
-
-
-            _agent = Agent<WorkflowState>.Start(inbox =>
-            {
-                Action loop = null;
-                loop = () =>
-                {
-                    var message = inbox.Receive();
-                    var process=message.Then(Process,inbox.CancellationToken);                    
-                    inbox.LoopAsync(process,loop,ex=>
-                        Log.ErrorFormat("[ERROR] File Event Processing:\r{0}", ex));
-                };
-                loop();
-            });*/
-        }
-
-/*
-        private Task<object> Process(WorkflowState state)
-        {
-            if (state==null)
-                throw new ArgumentNullException("state");
-            Contract.EndContractBlock();
-
-            if (Ignore(state.Path))
-                return CompletedTask<object>.Default;
-
-            var networkState = NetworkGate.GetNetworkState(state.Path);
-            //Skip if the file is already being downloaded or uploaded and 
-            //the change is create or modify
-            if (networkState != NetworkOperation.None &&
-                (
-                    state.TriggeringChange == WatcherChangeTypes.Created ||
-                    state.TriggeringChange == WatcherChangeTypes.Changed
-                ))
-                return CompletedTask<object>.Default;
-
-            try
-            {
-                //StatusKeeper.EnsureFileState(state.Path);
-                
-                UpdateFileStatus(state);
-                UpdateOverlayStatus(state);
-                UpdateLastMD5(state);
-                WorkflowAgent.Post(state);
-            }
-            catch (IOException exc)
-            {
-                if (File.Exists(state.Path))
-                {
-                    Log.WarnFormat("File access error occured, retrying {0}\n{1}", state.Path, exc);
-                    _agent.Post(state);
-                }
-                else
-                {
-                    Log.WarnFormat("File {0} does not exist. Will be ignored\n{1}", state.Path, exc);
-                }
-            }
-            catch (Exception exc)
-            {
-                Log.WarnFormat("Error occured while indexing{0}. The file will be skipped\n{1}",
-                               state.Path, exc);
-            }
-            return CompletedTask<object>.Default;
-        }
-
-        public bool Pause
-        {
-            get { return _watcher == null || !_watcher.EnableRaisingEvents; }
-            set
-            {
-                if (_watcher != null)
-                    _watcher.EnableRaisingEvents = !value;                
-            }
-        }
-*/
-
-        public string CachePath { get; set; }
-
-        /*private List<string> _selectivePaths = new List<string>();
-        public List<string> SelectivePaths
-        {
-            get { return _selectivePaths; }
-            set { _selectivePaths = value; }
-        }
-*/
-        public Selectives Selectives { get; set; }
-
-
-/*
-        public void Post(WorkflowState workflowState)
-        {
-            if (workflowState == null)
-                throw new ArgumentNullException("workflowState");
-            Contract.EndContractBlock();
-
-            _agent.Post(workflowState);
-        }
-
-        public void Stop()
-        {
-            if (_watcher != null)
-            {
-                _watcher.Dispose();
-            }
-            _watcher = null;
-
-            if (_agent!=null)
-                _agent.Stop();
-        }
-
-*/
-        // Enumerate all files in the Pithos directory except those in the Fragment folder
-        // and files with a .ignore extension
-        public IEnumerable<string> EnumerateFiles(string searchPattern="*")
-        {
-            var monitoredFiles = from filePath in Directory.EnumerateFileSystemEntries(RootPath, searchPattern, SearchOption.AllDirectories)
-                                 where !Ignore(filePath)
-                                 orderby filePath ascending 
-                                 select filePath;
-            return monitoredFiles;
-        }
-
-        public IEnumerable<FileInfo> EnumerateFileInfos(string searchPattern="*")
-        {
-            var rootDir = new DirectoryInfo(RootPath);
-            var monitoredFiles = from file in rootDir.EnumerateFiles(searchPattern, SearchOption.AllDirectories)
-                                 where !Ignore(file.FullName)
-                                 orderby file.FullName ascending 
-                                 select file;
-            return monitoredFiles;
-        }                
-
-        public IEnumerable<FileSystemInfo> EnumerateFileSystemInfos(string searchPattern="*")
-        {
-            var rootDir = new DirectoryInfo(RootPath);
-            //Ensure folders appear first, to allow folder processing as soon as possilbe
-            var folders = (from file in rootDir.EnumerateDirectories(searchPattern, SearchOption.AllDirectories)
-                                     where !Ignore(file.FullName)
-                                     orderby file.FullName ascending
-                                     select file).ToList();
-            var files = (from file in rootDir.EnumerateFiles(searchPattern, SearchOption.AllDirectories)
-                                  where !Ignore(file.FullName)
-                                  orderby file.Length ascending
-                                  select file as FileSystemInfo).ToList();
-            var monitoredFiles = folders
-                                 //Process small files first, leaving expensive large files for last
-                                 .Concat(files);
-            return monitoredFiles;
-        }                
-
-        public IEnumerable<string> EnumerateFilesAsRelativeUrls(string searchPattern="*")
-        {
-            var rootDir = new DirectoryInfo(RootPath);
-            var monitoredFiles = from file in rootDir.EnumerateFiles(searchPattern, SearchOption.AllDirectories)
-                                 where !Ignore(file.FullName)
-                                 orderby file.FullName ascending 
-                                 select file.AsRelativeUrlTo(RootPath);
-            return monitoredFiles;
-        }                
-
-        public IEnumerable<string> EnumerateFilesSystemInfosAsRelativeUrls(string searchPattern="*")
-        {
-            var rootDir = new DirectoryInfo(RootPath);
-            var monitoredFiles = from file in rootDir.EnumerateFileSystemInfos(searchPattern, SearchOption.AllDirectories)
-                                 where !Ignore(file.FullName)
-                                 orderby file.FullName ascending 
-                                 select file.AsRelativeUrlTo(RootPath);
-            return monitoredFiles;
-        }                
-
-
-        
-
-        public bool Ignore(string filePath)
-        {
-            if (IgnorePaths(filePath)) return true;
-
-
-            //If selective sync is enabled, 
-            if (IsUnselectedRootFolder(filePath))
-                    return false;
-            //Ignore if selective synchronization is defined, 
-            //And the target file is not below any of the selective paths
-            var ignore = !Selectives.IsSelected(AccountInfo, filePath);
-            return ignore;
-        }
-
-        public bool IsUnselectedRootFolder(string filePath)
-        {
-            return Selectives.IsSelectiveEnabled(AccountInfo.AccountKey) //propagate folder events 
-                   && Directory.Exists(filePath) //from the container root folder only. Note, in the first level below the account root path are the containers
-                   && FoundBelowRoot(filePath, RootPath, 2);
-        }
-
-        public bool IgnorePaths(string filePath)
-        {
-//Ignore all first-level directories and files (ie at the container folders level)
-            if (FoundBelowRoot(filePath, RootPath, 1))
-                return true;
-
-            //Ignore first-level items under the "others" folder (ie at the accounts folders level).
-            var othersPath = Path.Combine(RootPath, FolderConstants.OthersFolder);
-            if (FoundBelowRoot(filePath, othersPath, 1))
-                return true;
-
-            //Ignore second-level (container) folders under the "others" folder (ie at the container folders level). 
-            if (FoundBelowRoot(filePath, othersPath, 2))
-                return true;
-
-
-            //Ignore anything happening in the cache path
-            if (filePath.StartsWith(CachePath))
-                return true;
-            
-            //Finally, ignore events about one of the ignored files
-            return _ignoreFiles.ContainsKey(filePath.ToLower());
-        }
-
-/*        private static bool FoundInRoot(string filePath, string rootPath)
-        {
-            //var rootDirectory = new DirectoryInfo(rootPath);
-
-            //If the paths are equal, return true
-            if (filePath.Equals(rootPath, StringComparison.InvariantCultureIgnoreCase))
-                return true;
-
-            //If the filepath is below the root path
-            if (filePath.StartsWith(rootPath,StringComparison.InvariantCulture))
-            {
-                //Get the relative path
-                var relativePath = filePath.Substring(rootPath.Length + 1);
-                //If the relativePath does NOT contains a path separator, we found a match
-                return (!relativePath.Contains(@"\"));
-            }
-
-            //If the filepath is not under the root path, return false
-            return false;            
-        }*/
-
-
-        private static bool FoundBelowRoot(string filePath, string rootPath,int level)
-        {
-            //var rootDirectory = new DirectoryInfo(rootPath);
-
-            //If the paths are equal, return true
-            if (filePath.Equals(rootPath, StringComparison.InvariantCultureIgnoreCase))
-                return true;
-
-            //If the filepath is below the root path
-            if (filePath.StartsWith(rootPath,StringComparison.InvariantCulture))
-            {
-                //Get the relative path
-                var relativePath = filePath.Substring(rootPath.Length + 1);
-                //If the relativePath does NOT contains a path separator, we found a match
-                var levels=relativePath.ToCharArray().Count(c=>c=='\\')+1;                
-                return levels==level;
-            }
-
-            //If the filepath is not under the root path, return false
-            return false;            
-        }
-
-        /*
-        //Post a Change message for renames containing the old and new names
-        void OnRenameEvent(object sender, RenamedEventArgs e)
-        {
-            var oldFullPath = e.OldFullPath;
-            var fullPath = e.FullPath;
-            if (Ignore(oldFullPath) || Ignore(fullPath))
-                return;
-
-            _agent.Post(new WorkflowState
-            {
-                AccountInfo=AccountInfo,
-                OldPath = oldFullPath,
-                OldFileName = e.OldName,
-                Path = fullPath,
-                FileName = e.Name,
-                TriggeringChange = e.ChangeType
-            });
-        }
-        */
-
-        //Post a Change message for all events except rename
-        void OnFileEvent(object sender, FileSystemEventArgs e)
-        {
-            //Ignore events that affect the cache folder
-            var filePath = e.FullPath;
-            if (Ignore(filePath))
-                return;
-            _eventIdleBatch.Post(e);
-        }
-
-        //Post a Change message for moves containing the old and new names
-        void OnMoveEvent(object sender, MovedEventArgs e)
-        {
-            var oldFullPath = e.OldFullPath;
-            var fullPath = e.FullPath;
-            
-
-            //If the source path is one of the ignored folders, ignore
-            if (IgnorePaths(oldFullPath)) 
-                return;
-
-            //TODO: Must prevent move propagation if the source folder is blocked by selective sync
-            //Ignore takes into account Selective Sync
-            if (Ignore(fullPath))
-                return;
-            PollAgent.PostMove(e);
-            _eventIdleBatch.Post(e);
-        }
-
-
-
-        private Dictionary<WatcherChangeTypes, FileStatus> _statusDict = new Dictionary<WatcherChangeTypes, FileStatus>
-                                                                             {
-            {WatcherChangeTypes.Created,FileStatus.Created},
-            {WatcherChangeTypes.Changed,FileStatus.Modified},
-            {WatcherChangeTypes.Deleted,FileStatus.Deleted},
-            {WatcherChangeTypes.Renamed,FileStatus.Renamed}
-        };
-
-        private Dictionary<string, string> _ignoreFiles=new Dictionary<string, string>();
-
-        private WorkflowState UpdateFileStatus(WorkflowState state)
-        {
-            if (state==null)
-                throw new ArgumentNullException("state");
-            if (String.IsNullOrWhiteSpace(state.Path))
-                throw new ArgumentException("The state's Path can't be empty","state");
-            Contract.EndContractBlock();
-
-            var path = state.Path;
-            var status = _statusDict[state.TriggeringChange];
-            var oldStatus = Workflow.StatusKeeper.GetFileStatus(path);
-            if (status == oldStatus)
-            {
-                state.Status = status;
-                state.Skip = true;
-                return state;
-            }
-            if (state.Status == FileStatus.Renamed)
-                Workflow.ClearFileStatus(path);
-
-            state.Status = Workflow.SetFileStatus(path, status);
-            return state;
-        }
-
-        private WorkflowState UpdateOverlayStatus(WorkflowState state)
-        {
-            if (state==null)
-                throw new ArgumentNullException("state");
-            Contract.EndContractBlock();
-
-            if (state.Skip)
-                return state;
-
-            switch (state.Status)
-            {
-                case FileStatus.Created:
-                    this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Modified,state.ETag).Wait();
-                    break;
-                case FileStatus.Modified:
-                    this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Modified, state.ETag).Wait();
-                    break;
-                case FileStatus.Deleted:
-                    //this.StatusAgent.RemoveFileOverlayStatus(state.Path);
-                    break;
-                case FileStatus.Renamed:
-                    this.StatusKeeper.ClearFileStatus(state.OldPath);
-                    this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Modified, state.ETag).Wait();
-                    break;
-                case FileStatus.Unchanged:
-                    this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Normal, state.ETag).Wait();
-                    break;
-            }
-
-            if (state.Status == FileStatus.Deleted)
-                NativeMethods.RaiseChangeNotification(Path.GetDirectoryName(state.Path));
-            else
-                NativeMethods.RaiseChangeNotification(state.Path);
-            return state;
-        }
-
-
-        private WorkflowState UpdateFileChecksum(WorkflowState state)
-        {
-            if (state.Skip)
-                return state;
-
-            if (state.Status == FileStatus.Deleted)
-                return state;
-
-            var path = state.Path;
-            //Skip calculation for folders
-            if (Directory.Exists(path))
-                return state;
-
-            var info = new FileInfo(path);
-
-            using (StatusNotification.GetNotifier("Hashing {0}", "Finished Hashing {0}", info.Name))
-            {
-
-                var etag = info.ComputeShortHash(StatusNotification);
-
-                var progress = new Progress<double>(d =>
-                    StatusNotification.Notify(new StatusNotification(String.Format("Hashing {0} of {1}", d, info.Name))));
-
-                string merkleHash = info.CalculateHash(StatusKeeper.BlockSize, StatusKeeper.BlockHash,PollAgent.CancellationToken,progress);
-                StatusKeeper.UpdateFileChecksum(path, etag, merkleHash);
-
-                state.Hash = merkleHash;
-                return state;
-            }
-        }
-
-        //Does the file exist in the container's local folder?
-        public bool Exists(string relativePath)
-        {
-            if (String.IsNullOrWhiteSpace(relativePath))
-                throw new ArgumentNullException("relativePath");
-            //A RootPath must be set before calling this method
-            if (String.IsNullOrWhiteSpace(RootPath))
-                throw new InvalidOperationException("RootPath was not set");
-            Contract.EndContractBlock();
-            //Create the absolute path by combining the RootPath with the relativePath
-            var absolutePath=Path.Combine(RootPath, relativePath);
-            //Is this a valid file?
-            if (File.Exists(absolutePath))
-                return true;
-            //Or a directory?
-            if (Directory.Exists(absolutePath))
-                return true;
-            //Fail if it is neither
-            return false;
-        }
-
-        public static FileAgent GetFileAgent(AccountInfo accountInfo)
-        {
-            return GetFileAgent(accountInfo.AccountPath);
-        }
-
-        public static FileAgent GetFileAgent(string rootPath)
-        {
-            return AgentLocator<FileAgent>.Get(rootPath.ToLower());
-        }
-
-
-        public FileSystemInfo GetFileSystemInfo(string relativePath)
-        {
-            if (String.IsNullOrWhiteSpace(relativePath))
-                throw new ArgumentNullException("relativePath");
-            //A RootPath must be set before calling this method
-            if (String.IsNullOrWhiteSpace(RootPath))
-                throw new InvalidOperationException("RootPath was not set");            
-            Contract.EndContractBlock();            
-
-            var absolutePath = Path.Combine(RootPath, relativePath);
-
-            if (Directory.Exists(absolutePath))
-                return new DirectoryInfo(absolutePath).WithProperCapitalization();
-            else
-                return new FileInfo(absolutePath).WithProperCapitalization();
-            
-        }
-
-        public void Delete(string relativePath)
-        {
-            var absolutePath = Path.Combine(RootPath, relativePath).ToLower();
-            if (Log.IsDebugEnabled)
-                Log.DebugFormat("Deleting {0}", absolutePath);
-            if (File.Exists(absolutePath))
-            {    
-                try
-                {
-                    File.Delete(absolutePath);
-                }
-                //The file may have been deleted by another thread. Just ignore the relevant exception
-                catch (FileNotFoundException) { }
-            }
-            else if (Directory.Exists(absolutePath))
-            {
-                DeleteWithRetry(absolutePath, 3);
-            }
-
-            //_ignoreFiles[absolutePath] = absolutePath;                
-            StatusKeeper.ClearFileStatus(absolutePath);
-        }
-
-        private static void DeleteWithRetry(string absolutePath, int retries)
-        {
-            try
-            {
-                var dirinfo = new DirectoryInfo(absolutePath);
-                dirinfo.Attributes &= ~FileAttributes.ReadOnly;
-                foreach (var info in dirinfo.EnumerateFileSystemInfos("*", SearchOption.AllDirectories))
-                {
-                    info.Attributes &= ~FileAttributes.ReadOnly;
-                }
-                dirinfo.Refresh();
-                dirinfo.Delete(true);
-            }
-            //The directory may have been deleted by another thread. Just ignore the relevant exception
-            catch (DirectoryNotFoundException) { }
-            catch (IOException)
-            {
-                if (retries>0)
-                    DeleteWithRetry(absolutePath,retries-1);
-                else
-                {
-                    throw;
-                }
-            }
-        }
-    }
-}
+#region\r
+/* -----------------------------------------------------------------------\r
+ * <copyright file="FileAgent.cs" company="GRNet">\r
+ * \r
+ * Copyright 2011-2012 GRNET S.A. All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or\r
+ * without modification, are permitted provided that the following\r
+ * conditions are met:\r
+ *\r
+ *   1. Redistributions of source code must retain the above\r
+ *      copyright notice, this list of conditions and the following\r
+ *      disclaimer.\r
+ *\r
+ *   2. Redistributions in binary form must reproduce the above\r
+ *      copyright notice, this list of conditions and the following\r
+ *      disclaimer in the documentation and/or other materials\r
+ *      provided with the distribution.\r
+ *\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS\r
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR\r
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF\r
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\r
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\r
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
+ * POSSIBILITY OF SUCH DAMAGE.\r
+ *\r
+ * The views and conclusions contained in the software and\r
+ * documentation are those of the authors and should not be\r
+ * interpreted as representing official policies, either expressed\r
+ * or implied, of GRNET S.A.\r
+ * </copyright>\r
+ * -----------------------------------------------------------------------\r
+ */\r
+#endregion\r
+using System;\r
+using System.Collections.Generic;\r
+using System.Diagnostics.Contracts;\r
+using System.IO;\r
+using System.Linq;\r
+using System.Reflection;\r
+using System.Threading.Tasks;\r
+using Pithos.Interfaces;\r
+using Pithos.Network;\r
+using log4net;\r
+\r
+namespace Pithos.Core.Agents\r
+{\r
+//    [Export]\r
+    public class FileAgent\r
+    {\r
+        private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);\r
+\r
+        /*\r
+                Agent<WorkflowState> _agent;\r
+        */\r
+        private FileSystemWatcher _watcher;\r
+        private FileSystemWatcherAdapter _adapter;\r
+        private FileEventIdleBatch _eventIdleBatch;\r
+\r
+        //[Import]\r
+        public IStatusKeeper StatusKeeper { get; set; }\r
+\r
+        public IStatusNotification StatusNotification { get; set; }\r
+        //[Import]\r
+        public IPithosWorkflow Workflow { get; set; }\r
+        //[Import]\r
+        //public WorkflowAgent WorkflowAgent { get; set; }\r
+\r
+        private AccountInfo AccountInfo { get; set; }\r
+\r
+        internal string RootPath { get;  set; }\r
+        \r
+        public TimeSpan IdleTimeout { get; set; }\r
+\r
+        public PollAgent PollAgent { get; set; }\r
+\r
+        private void ProcessBatchedEvents(FileSystemEventArgs[] fileEvents)\r
+        {\r
+            var paths = new HashSet<string>();\r
+\r
+            foreach (var evt in fileEvents)\r
+            {\r
+                paths.Add(evt.FullPath);\r
+                if (evt is MovedEventArgs)\r
+                {\r
+                    paths.Add((evt as MovedEventArgs).OldFullPath);\r
+                }\r
+                else if (evt is RenamedEventArgs)\r
+                {\r
+                    paths.Add((evt as RenamedEventArgs).OldFullPath);\r
+                }\r
+            }\r
+                  \r
+            PollAgent.SynchNow(paths);\r
+        }\r
+\r
+/*\r
+        private void ProcessBatchedEvents(Dictionary<string, FileSystemEventArgs[]> fileEvents)\r
+        {\r
+            StatusNotification.SetPithosStatus(PithosStatus.LocalSyncing,String.Format("Uploading {0} files",fileEvents.Count));\r
+            //Start with events that do not originate in one of the ignored folders\r
+            var initialEvents = from evt in fileEvents\r
+                              where !IgnorePaths(evt.Key)\r
+                              select evt;\r
+\r
+            IEnumerable<KeyValuePair<string, FileSystemEventArgs[]>> cleanEvents;\r
+\r
+            \r
+            var selectiveEnabled = Selectives.IsSelectiveEnabled(AccountInfo.AccountKey);\r
+            //When selective sync is enabled,\r
+            if (selectiveEnabled)\r
+            {\r
+                //Include all selected items\r
+                var selectedEvents = from evt in initialEvents\r
+                                     where Selectives.IsSelected(AccountInfo, evt.Key)\r
+                                     select evt;                \r
+                //And all folder creations in the unselected folders\r
+                var folderCreations = from evt in initialEvents\r
+                                      let folderPath=evt.Key\r
+                                      //The original folder may not exist due to renames. Just make sure that the path is not a file\r
+                                      where !File.Exists(folderPath)\r
+                                            //We only want unselected items\r
+                                            && !Selectives.IsSelected(AccountInfo, folderPath)\r
+                                            //Is there any creation event related to the folder?\r
+                                            && evt.Value.Any(arg => arg.ChangeType == WatcherChangeTypes.Created)\r
+                                      select evt;\r
+                cleanEvents = selectedEvents.Union(folderCreations).ToList();\r
+            }\r
+            //If selective is disabled, only exclude the shared folders \r
+            else\r
+            {\r
+                cleanEvents = (from evt in initialEvents\r
+                              where !evt.Key.IsSharedTo(AccountInfo)\r
+                              select evt).ToList();\r
+            }\r
+\r
+\r
+            foreach (var fileEvent in cleanEvents)\r
+            {\r
+                //var filePath = fileEvent.Key;\r
+                var changes = fileEvent.Value;\r
+\r
+                var isNotFile = !File.Exists(fileEvent.Key);\r
+                foreach (var change in changes)\r
+                {\r
+                    if (change.ChangeType == WatcherChangeTypes.Renamed)\r
+                    {\r
+                        var rename = (MovedEventArgs) change;\r
+                        _agent.Post(new WorkflowState(change)\r
+                                        {\r
+                                            AccountInfo = AccountInfo,\r
+                                            OldPath = rename.OldFullPath,\r
+                                            OldFileName = Path.GetFileName(rename.OldName),\r
+                                            Path = rename.FullPath,\r
+                                            FileName = Path.GetFileName(rename.Name),\r
+                                            TriggeringChange = rename.ChangeType\r
+                                        });\r
+                    }\r
+                    else\r
+                    {\r
+                        var isCreation = selectiveEnabled && isNotFile && change.ChangeType == WatcherChangeTypes.Created;\r
+                        _agent.Post(new WorkflowState(change)\r
+                                        {\r
+                                            AccountInfo = AccountInfo,\r
+                                            Path = change.FullPath,\r
+                                            FileName = Path.GetFileName(change.Name),\r
+                                            TriggeringChange = change.ChangeType,\r
+                                            IsCreation=isCreation\r
+                                        });\r
+                    }\r
+                }\r
+            }\r
+            StatusNotification.SetPithosStatus(PithosStatus.LocalComplete);\r
+        }\r
+*/\r
+\r
+        public void Start(AccountInfo accountInfo,string rootPath)\r
+        {\r
+            if (accountInfo==null)\r
+                throw new ArgumentNullException("accountInfo");\r
+            if (String.IsNullOrWhiteSpace(rootPath))\r
+                throw new ArgumentNullException("rootPath");\r
+            if (!Path.IsPathRooted(rootPath))\r
+                throw new ArgumentException("rootPath must be an absolute path","rootPath");\r
+            if (IdleTimeout == null)\r
+                throw new InvalidOperationException("IdleTimeout must have a valid value");\r
+                Contract.EndContractBlock();\r
+\r
+            AccountInfo = accountInfo;\r
+            RootPath = rootPath;\r
+            \r
+            _eventIdleBatch = new FileEventIdleBatch(PollAgent,(int)IdleTimeout.TotalMilliseconds, ProcessBatchedEvents);\r
+\r
+            _watcher = new FileSystemWatcher(rootPath) { IncludeSubdirectories = true, InternalBufferSize = 8 * 4096 };\r
+            _adapter = new FileSystemWatcherAdapter(_watcher,this);\r
+\r
+            _adapter.Changed += OnFileEvent;\r
+            _adapter.Created += OnFileEvent;\r
+            _adapter.Deleted += OnFileEvent;\r
+            //_adapter.Renamed += OnRenameEvent;\r
+            _adapter.Moved += OnMoveEvent;\r
+            _watcher.EnableRaisingEvents = true;\r
+\r
+/*            \r
+\r
+\r
+\r
+            _agent = Agent<WorkflowState>.Start(inbox =>\r
+            {\r
+                Action loop = null;\r
+                loop = () =>\r
+                {\r
+                    var message = inbox.Receive();\r
+                    var process=message.Then(Process,inbox.CancellationToken);                    \r
+                    inbox.LoopAsync(process,loop,ex=>\r
+                        Log.ErrorFormat("[ERROR] File Event Processing:\r{0}", ex));\r
+                };\r
+                loop();\r
+            });*/\r
+        }\r
+\r
+/*\r
+        private Task<object> Process(WorkflowState state)\r
+        {\r
+            if (state==null)\r
+                throw new ArgumentNullException("state");\r
+            Contract.EndContractBlock();\r
+\r
+            if (Ignore(state.Path))\r
+                return CompletedTask<object>.Default;\r
+\r
+            var networkState = NetworkGate.GetNetworkState(state.Path);\r
+            //Skip if the file is already being downloaded or uploaded and \r
+            //the change is create or modify\r
+            if (networkState != NetworkOperation.None &&\r
+                (\r
+                    state.TriggeringChange == WatcherChangeTypes.Created ||\r
+                    state.TriggeringChange == WatcherChangeTypes.Changed\r
+                ))\r
+                return CompletedTask<object>.Default;\r
+\r
+            try\r
+            {\r
+                //StatusKeeper.EnsureFileState(state.Path);\r
+                \r
+                UpdateFileStatus(state);\r
+                UpdateOverlayStatus(state);\r
+                UpdateLastMD5(state);\r
+                WorkflowAgent.Post(state);\r
+            }\r
+            catch (IOException exc)\r
+            {\r
+                if (File.Exists(state.Path))\r
+                {\r
+                    Log.WarnFormat("File access error occured, retrying {0}\n{1}", state.Path, exc);\r
+                    _agent.Post(state);\r
+                }\r
+                else\r
+                {\r
+                    Log.WarnFormat("File {0} does not exist. Will be ignored\n{1}", state.Path, exc);\r
+                }\r
+            }\r
+            catch (Exception exc)\r
+            {\r
+                Log.WarnFormat("Error occured while indexing{0}. The file will be skipped\n{1}",\r
+                               state.Path, exc);\r
+            }\r
+            return CompletedTask<object>.Default;\r
+        }\r
+\r
+        public bool Pause\r
+        {\r
+            get { return _watcher == null || !_watcher.EnableRaisingEvents; }\r
+            set\r
+            {\r
+                if (_watcher != null)\r
+                    _watcher.EnableRaisingEvents = !value;                \r
+            }\r
+        }\r
+*/\r
+\r
+        public string CachePath { get; set; }\r
+\r
+        /*private List<string> _selectivePaths = new List<string>();\r
+        public List<string> SelectivePaths\r
+        {\r
+            get { return _selectivePaths; }\r
+            set { _selectivePaths = value; }\r
+        }\r
+*/\r
+        public Selectives Selectives { get; set; }\r
+\r
+\r
+/*\r
+        public void Post(WorkflowState workflowState)\r
+        {\r
+            if (workflowState == null)\r
+                throw new ArgumentNullException("workflowState");\r
+            Contract.EndContractBlock();\r
+\r
+            _agent.Post(workflowState);\r
+        }\r
+\r
+        public void Stop()\r
+        {\r
+            if (_watcher != null)\r
+            {\r
+                _watcher.Dispose();\r
+            }\r
+            _watcher = null;\r
+\r
+            if (_agent!=null)\r
+                _agent.Stop();\r
+        }\r
+\r
+*/\r
+        // Enumerate all files in the Pithos directory except those in the Fragment folder\r
+        // and files with a .ignore extension\r
+        public IEnumerable<string> EnumerateFiles(string searchPattern="*")\r
+        {\r
+            var monitoredFiles = from filePath in Directory.EnumerateFileSystemEntries(RootPath, searchPattern, SearchOption.AllDirectories)\r
+                                 where !Ignore(filePath)\r
+                                 orderby filePath ascending \r
+                                 select filePath;\r
+            return monitoredFiles;\r
+        }\r
+\r
+        public IEnumerable<FileInfo> EnumerateFileInfos(string searchPattern="*")\r
+        {\r
+            var rootDir = new DirectoryInfo(RootPath);\r
+            var monitoredFiles = from file in rootDir.EnumerateFiles(searchPattern, SearchOption.AllDirectories)\r
+                                 where !Ignore(file.FullName)\r
+                                 orderby file.FullName ascending \r
+                                 select file;\r
+            return monitoredFiles;\r
+        }                \r
+\r
+        public IEnumerable<FileSystemInfo> EnumerateFileSystemInfos(string searchPattern="*")\r
+        {\r
+            var rootDir = new DirectoryInfo(RootPath);\r
+            //Ensure folders appear first, to allow folder processing as soon as possilbe\r
+            var folders = (from file in rootDir.EnumerateDirectories(searchPattern, SearchOption.AllDirectories)\r
+                                     where !Ignore(file.FullName)\r
+                                     orderby file.FullName ascending\r
+                                     select file).ToList();\r
+            var files = (from file in rootDir.EnumerateFiles(searchPattern, SearchOption.AllDirectories)\r
+                                  where !Ignore(file.FullName)\r
+                                  orderby file.Length ascending\r
+                                  select file as FileSystemInfo).ToList();\r
+            var monitoredFiles = folders\r
+                                 //Process small files first, leaving expensive large files for last\r
+                                 .Concat(files);\r
+            return monitoredFiles;\r
+        }                \r
+\r
+        public IEnumerable<string> EnumerateFilesAsRelativeUrls(string searchPattern="*")\r
+        {\r
+            var rootDir = new DirectoryInfo(RootPath);\r
+            var monitoredFiles = from file in rootDir.EnumerateFiles(searchPattern, SearchOption.AllDirectories)\r
+                                 where !Ignore(file.FullName)\r
+                                 orderby file.FullName ascending \r
+                                 select file.AsRelativeUrlTo(RootPath);\r
+            return monitoredFiles;\r
+        }                \r
+\r
+        public IEnumerable<string> EnumerateFilesSystemInfosAsRelativeUrls(string searchPattern="*")\r
+        {\r
+            var rootDir = new DirectoryInfo(RootPath);\r
+            var monitoredFiles = from file in rootDir.EnumerateFileSystemInfos(searchPattern, SearchOption.AllDirectories)\r
+                                 where !Ignore(file.FullName)\r
+                                 orderby file.FullName ascending \r
+                                 select file.AsRelativeUrlTo(RootPath);\r
+            return monitoredFiles;\r
+        }                \r
+\r
+\r
+        \r
+\r
+        public bool Ignore(string filePath)\r
+        {\r
+            if (IgnorePaths(filePath)) return true;\r
+\r
+\r
+            //If selective sync is enabled, \r
+            if (IsUnselectedRootFolder(filePath))\r
+                    return false;\r
+            //Ignore if selective synchronization is defined, \r
+            //And the target file is not below any of the selective paths\r
+            var ignore = !Selectives.IsSelected(AccountInfo, filePath);\r
+            return ignore;\r
+        }\r
+\r
+        public bool IsUnselectedRootFolder(string filePath)\r
+        {\r
+            return Selectives.IsSelectiveEnabled(AccountInfo.AccountKey) //propagate folder events \r
+                   && Directory.Exists(filePath) //from the container root folder only. Note, in the first level below the account root path are the containers\r
+                   && FoundBelowRoot(filePath, RootPath, 2);\r
+        }\r
+\r
+        public bool IgnorePaths(string filePath)\r
+        {\r
+//Ignore all first-level directories and files (ie at the container folders level)\r
+            if (FoundBelowRoot(filePath, RootPath, 1))\r
+                return true;\r
+\r
+            //Ignore first-level items under the "others" folder (ie at the accounts folders level).\r
+            var othersPath = Path.Combine(RootPath, FolderConstants.OthersFolder);\r
+            if (FoundBelowRoot(filePath, othersPath, 1))\r
+                return true;\r
+\r
+            //Ignore second-level (container) folders under the "others" folder (ie at the container folders level). \r
+            if (FoundBelowRoot(filePath, othersPath, 2))\r
+                return true;\r
+\r
+\r
+            //Ignore anything happening in the cache path\r
+            if (filePath.StartsWith(CachePath))\r
+                return true;\r
+            \r
+            //Finally, ignore events about one of the ignored files\r
+            return _ignoreFiles.ContainsKey(filePath.ToLower());\r
+        }\r
+\r
+/*        private static bool FoundInRoot(string filePath, string rootPath)\r
+        {\r
+            //var rootDirectory = new DirectoryInfo(rootPath);\r
+\r
+            //If the paths are equal, return true\r
+            if (filePath.Equals(rootPath, StringComparison.InvariantCultureIgnoreCase))\r
+                return true;\r
+\r
+            //If the filepath is below the root path\r
+            if (filePath.StartsWith(rootPath,StringComparison.InvariantCulture))\r
+            {\r
+                //Get the relative path\r
+                var relativePath = filePath.Substring(rootPath.Length + 1);\r
+                //If the relativePath does NOT contains a path separator, we found a match\r
+                return (!relativePath.Contains(@"\"));\r
+            }\r
+\r
+            //If the filepath is not under the root path, return false\r
+            return false;            \r
+        }*/\r
+\r
+\r
+        private static bool FoundBelowRoot(string filePath, string rootPath,int level)\r
+        {\r
+            //var rootDirectory = new DirectoryInfo(rootPath);\r
+\r
+            //If the paths are equal, return true\r
+            if (filePath.Equals(rootPath, StringComparison.InvariantCultureIgnoreCase))\r
+                return true;\r
+\r
+            //If the filepath is below the root path\r
+            if (filePath.StartsWith(rootPath,StringComparison.InvariantCulture))\r
+            {\r
+                //Get the relative path\r
+                var relativePath = filePath.Substring(rootPath.Length + 1);\r
+                //If the relativePath does NOT contains a path separator, we found a match\r
+                var levels=relativePath.ToCharArray().Count(c=>c=='\\')+1;                \r
+                return levels==level;\r
+            }\r
+\r
+            //If the filepath is not under the root path, return false\r
+            return false;            \r
+        }\r
+\r
+        /*\r
+        //Post a Change message for renames containing the old and new names\r
+        void OnRenameEvent(object sender, RenamedEventArgs e)\r
+        {\r
+            var oldFullPath = e.OldFullPath;\r
+            var fullPath = e.FullPath;\r
+            if (Ignore(oldFullPath) || Ignore(fullPath))\r
+                return;\r
+\r
+            _agent.Post(new WorkflowState\r
+            {\r
+                AccountInfo=AccountInfo,\r
+                OldPath = oldFullPath,\r
+                OldFileName = e.OldName,\r
+                Path = fullPath,\r
+                FileName = e.Name,\r
+                TriggeringChange = e.ChangeType\r
+            });\r
+        }\r
+        */\r
+\r
+        //Post a Change message for all events except rename\r
+        void OnFileEvent(object sender, FileSystemEventArgs e)\r
+        {\r
+            //Ignore events that affect the cache folder\r
+            var filePath = e.FullPath;\r
+            if (Ignore(filePath))\r
+                return;\r
+            _eventIdleBatch.Post(e);\r
+        }\r
+\r
+        //Post a Change message for moves containing the old and new names\r
+        void OnMoveEvent(object sender, MovedEventArgs e)\r
+        {\r
+            var oldFullPath = e.OldFullPath;\r
+            var fullPath = e.FullPath;\r
+            \r
+\r
+            //If the source path is one of the ignored folders, ignore\r
+            if (IgnorePaths(oldFullPath)) \r
+                return;\r
+\r
+            //TODO: Must prevent move propagation if the source folder is blocked by selective sync\r
+            //Ignore takes into account Selective Sync\r
+            if (Ignore(fullPath))\r
+                return;\r
+            PollAgent.PostMove(e);\r
+            _eventIdleBatch.Post(e);\r
+        }\r
+\r
+\r
+\r
+        private Dictionary<WatcherChangeTypes, FileStatus> _statusDict = new Dictionary<WatcherChangeTypes, FileStatus>\r
+                                                                             {\r
+            {WatcherChangeTypes.Created,FileStatus.Created},\r
+            {WatcherChangeTypes.Changed,FileStatus.Modified},\r
+            {WatcherChangeTypes.Deleted,FileStatus.Deleted},\r
+            {WatcherChangeTypes.Renamed,FileStatus.Renamed}\r
+        };\r
+\r
+        private Dictionary<string, string> _ignoreFiles=new Dictionary<string, string>();\r
+\r
+        private WorkflowState UpdateFileStatus(WorkflowState state)\r
+        {\r
+            if (state==null)\r
+                throw new ArgumentNullException("state");\r
+            if (String.IsNullOrWhiteSpace(state.Path))\r
+                throw new ArgumentException("The state's Path can't be empty","state");\r
+            Contract.EndContractBlock();\r
+\r
+            var path = state.Path;\r
+            var status = _statusDict[state.TriggeringChange];\r
+            var oldStatus = Workflow.StatusKeeper.GetFileStatus(path);\r
+            if (status == oldStatus)\r
+            {\r
+                state.Status = status;\r
+                state.Skip = true;\r
+                return state;\r
+            }\r
+            if (state.Status == FileStatus.Renamed)\r
+                Workflow.ClearFileStatus(path);\r
+\r
+            state.Status = Workflow.SetFileStatus(path, status);\r
+            return state;\r
+        }\r
+\r
+    /*    private WorkflowState UpdateOverlayStatus(WorkflowState state)\r
+        {\r
+            if (state==null)\r
+                throw new ArgumentNullException("state");\r
+            Contract.EndContractBlock();\r
+\r
+            if (state.Skip)\r
+                return state;\r
+\r
+            switch (state.Status)\r
+            {\r
+                case FileStatus.Created:\r
+                    this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Modified,state.ETag).Wait();\r
+                    break;\r
+                case FileStatus.Modified:\r
+                    this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Modified, state.ETag).Wait();\r
+                    break;\r
+                case FileStatus.Deleted:\r
+                    //this.StatusAgent.RemoveFileOverlayStatus(state.Path);\r
+                    break;\r
+                case FileStatus.Renamed:\r
+                    this.StatusKeeper.ClearFileStatus(state.OldPath);\r
+                    this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Modified, state.ETag).Wait();\r
+                    break;\r
+                case FileStatus.Unchanged:\r
+                    this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Normal, state.ETag).Wait();\r
+                    break;\r
+            }\r
+\r
+            if (state.Status == FileStatus.Deleted)\r
+                NativeMethods.RaiseChangeNotification(Path.GetDirectoryName(state.Path));\r
+            else\r
+                NativeMethods.RaiseChangeNotification(state.Path);\r
+            return state;\r
+        }\r
+\r
+\r
+        private WorkflowState UpdateFileChecksum(WorkflowState state)\r
+        {\r
+            if (state.Skip)\r
+                return state;\r
+\r
+            if (state.Status == FileStatus.Deleted)\r
+                return state;\r
+\r
+            var path = state.Path;\r
+            //Skip calculation for folders\r
+            if (Directory.Exists(path))\r
+                return state;\r
+\r
+            var info = new FileInfo(path);\r
+\r
+            using (StatusNotification.GetNotifier("Hashing {0}", "Finished Hashing {0}", info.Name))\r
+            {\r
+\r
+                var etag = info.ComputeShortHash(StatusNotification);\r
+\r
+                var progress = new Progress<double>(d =>\r
+                    StatusNotification.Notify(new StatusNotification(String.Format("Hashing {0} of {1}", d, info.Name))));\r
+\r
+                string merkleHash = info.CalculateHash(StatusKeeper.BlockSize, StatusKeeper.BlockHash,PollAgent.CancellationToken,progress);\r
+                StatusKeeper.UpdateFileChecksum(path, etag, merkleHash);\r
+\r
+                state.Hash = merkleHash;\r
+                return state;\r
+            }\r
+        }*/\r
+\r
+        //Does the file exist in the container's local folder?\r
+        public bool Exists(string relativePath)\r
+        {\r
+            if (String.IsNullOrWhiteSpace(relativePath))\r
+                throw new ArgumentNullException("relativePath");\r
+            //A RootPath must be set before calling this method\r
+            if (String.IsNullOrWhiteSpace(RootPath))\r
+                throw new InvalidOperationException("RootPath was not set");\r
+            Contract.EndContractBlock();\r
+            //Create the absolute path by combining the RootPath with the relativePath\r
+            var absolutePath=Path.Combine(RootPath, relativePath);\r
+            //Is this a valid file?\r
+            if (File.Exists(absolutePath))\r
+                return true;\r
+            //Or a directory?\r
+            if (Directory.Exists(absolutePath))\r
+                return true;\r
+            //Fail if it is neither\r
+            return false;\r
+        }\r
+\r
+        public static FileAgent GetFileAgent(AccountInfo accountInfo)\r
+        {\r
+            return GetFileAgent(accountInfo.AccountPath);\r
+        }\r
+\r
+        public static FileAgent GetFileAgent(string rootPath)\r
+        {\r
+            return AgentLocator<FileAgent>.Get(rootPath.ToLower());\r
+        }\r
+\r
+\r
+        public FileSystemInfo GetFileSystemInfo(string relativePath)\r
+        {\r
+            if (String.IsNullOrWhiteSpace(relativePath))\r
+                throw new ArgumentNullException("relativePath");\r
+            //A RootPath must be set before calling this method\r
+            if (String.IsNullOrWhiteSpace(RootPath))\r
+                throw new InvalidOperationException("RootPath was not set");            \r
+            Contract.EndContractBlock();            \r
+\r
+            var absolutePath = Path.Combine(RootPath, relativePath);\r
+\r
+            if (Directory.Exists(absolutePath))\r
+                return new DirectoryInfo(absolutePath).WithProperCapitalization();\r
+            else\r
+                return new FileInfo(absolutePath).WithProperCapitalization();\r
+            \r
+        }\r
+\r
+        public void Delete(string relativePath)\r
+        {\r
+            var absolutePath = Path.Combine(RootPath, relativePath).ToLower();\r
+            if (Log.IsDebugEnabled)\r
+                Log.DebugFormat("Deleting {0}", absolutePath);\r
+            if (File.Exists(absolutePath))\r
+            {    \r
+                try\r
+                {\r
+                    File.Delete(absolutePath);\r
+                }\r
+                //The file may have been deleted by another thread. Just ignore the relevant exception\r
+                catch (FileNotFoundException) { }\r
+            }\r
+            else if (Directory.Exists(absolutePath))\r
+            {\r
+                DeleteWithRetry(absolutePath, 3);\r
+            }\r
+\r
+            //_ignoreFiles[absolutePath] = absolutePath;                \r
+            StatusKeeper.ClearFileStatus(absolutePath);\r
+        }\r
+\r
+        private static void DeleteWithRetry(string absolutePath, int retries)\r
+        {\r
+            try\r
+            {\r
+                var dirinfo = new DirectoryInfo(absolutePath);\r
+                dirinfo.Attributes &= ~FileAttributes.ReadOnly;\r
+                foreach (var info in dirinfo.EnumerateFileSystemInfos("*", SearchOption.AllDirectories))\r
+                {\r
+                    info.Attributes &= ~FileAttributes.ReadOnly;\r
+                }\r
+                dirinfo.Refresh();\r
+                dirinfo.Delete(true);\r
+            }\r
+            //The directory may have been deleted by another thread. Just ignore the relevant exception\r
+            catch (DirectoryNotFoundException) { }\r
+            catch (IOException)\r
+            {\r
+                if (retries>0)\r
+                    DeleteWithRetry(absolutePath,retries-1);\r
+                else\r
+                {\r
+                    throw;\r
+                }\r
+            }\r
+        }\r
+    }\r
+}\r