2 using System.Collections.Concurrent;
3 using System.Collections.Generic;
4 using System.ComponentModel.Composition;
5 using System.Diagnostics;
6 using System.Diagnostics.Contracts;
9 using System.Net.NetworkInformation;
10 using System.Security.Cryptography;
11 using System.ServiceModel.Description;
13 using System.Threading;
14 using System.Threading.Tasks;
15 using Castle.ActiveRecord.Queries;
16 using Microsoft.WindowsAPICodePack.Net;
17 using Pithos.Core.Agents;
18 using Pithos.Interfaces;
19 using System.ServiceModel;
25 [Export(typeof(PithosMonitor))]
26 public class PithosMonitor:IDisposable
28 private int _blockSize;
29 private string _blockHash;
32 public IPithosSettings Settings{get;set;}
35 public IStatusKeeper StatusKeeper { get; set; }
38 public IPithosWorkflow Workflow { get; set; }
40 public ICloudClient CloudClient { get; set; }
42 public IStatusNotification StatusNotification { get; set; }
45 public FileAgent FileAgent { get; set; }
48 public WorkflowAgent WorkflowAgent { get; set; }
51 public NetworkAgent NetworkAgent { get; set; }
53 public string UserName { get; set; }
54 public string ApiKey { get; set; }
56 private Network.AccountInfo _accountInfo;
59 private static readonly ILog Log = LogManager.GetLogger(typeof(PithosMonitor));
64 get { return FileAgent.Pause; }
67 FileAgent.Pause = value;
70 StatusKeeper.SetPithosStatus(PithosStatus.SyncPaused);
71 StatusNotification.NotifyChange("Paused");
75 StatusKeeper.SetPithosStatus(PithosStatus.InSynch);
76 StatusNotification.NotifyChange("Synchronizing");
81 private string _rootPath;
82 public string RootPath
84 get { return _rootPath; }
87 _rootPath = value.ToLower();
92 CancellationTokenSource _cancellationSource;
95 private bool _isInitialized;
99 if (String.IsNullOrWhiteSpace(ApiKey))
100 throw new InvalidOperationException("The ApiKey is empty");
101 if (String.IsNullOrWhiteSpace(UserName))
102 throw new InvalidOperationException("The UserName is empty");
103 if (String.IsNullOrWhiteSpace(AuthenticationUrl))
104 throw new InvalidOperationException("The Authentication url is empty");
105 Contract.EndContractBlock();
107 StatusNotification.NotifyChange("Starting");
110 if (!_cancellationSource.IsCancellationRequested)
113 _cancellationSource = new CancellationTokenSource();
115 CloudClient=new CloudFilesClient(UserName,ApiKey);
116 var proxyUri = ProxyFromSettings();
117 CloudClient.Proxy = proxyUri;
118 CloudClient.UsePithos = this.UsePithos;
119 CloudClient.AuthenticationUrl = this.AuthenticationUrl;
121 _accountInfo = CloudClient.Authenticate();
122 _accountInfo.AccountPath = RootPath;
125 EnsurePithosContainers();
127 StatusKeeper.BlockHash = _blockHash;
128 StatusKeeper.BlockSize = _blockSize;
130 StatusKeeper.StartProcessing(_cancellationSource.Token);
136 StartWorkflowAgent();
137 WorkflowAgent.RestartInterruptedFiles(_accountInfo);
138 _isInitialized = true;
141 private void EnsurePithosContainers()
144 //Create the two default containers if they are missing
145 var pithosContainers = new List<string>{ FolderConstants.TrashContainer,FolderConstants.PithosContainer};
146 foreach (var container in pithosContainers)
148 var info=CloudClient.GetContainerInfo(this.UserName, container);
149 if (info == ContainerInfo.Empty)
151 CloudClient.CreateContainer(this.UserName, container);
152 info = CloudClient.GetContainerInfo(this.UserName, container);
154 _blockSize = info.BlockSize;
155 _blockHash = info.BlockHash;
156 _accountInfo.BlockSize = _blockSize;
157 _accountInfo.BlockHash = _blockHash;
161 public string AuthenticationUrl { get; set; }
163 private Uri ProxyFromSettings()
165 if (Settings.UseManualProxy)
167 var proxyUri = new UriBuilder
169 Host = Settings.ProxyServer,
170 Port = Settings.ProxyPort
172 if (Settings.ProxyAuthentication)
174 proxyUri.UserName = Settings.ProxyUsername;
175 proxyUri.Password = Settings.ProxyPassword;
182 private void IndexLocalFiles()
184 StatusNotification.NotifyChange("Indexing Local Files",TraceLevel.Info);
185 using (log4net.ThreadContext.Stacks["Monitor"].Push("Indexing local files"))
190 var fragmentsPath = Path.Combine(RootPath, FolderConstants.FragmentsFolder);
191 var directory = new DirectoryInfo(RootPath);
193 from file in directory.EnumerateFiles("*", SearchOption.AllDirectories)
194 where !file.FullName.StartsWith(fragmentsPath, StringComparison.InvariantCultureIgnoreCase) &&
195 !file.Extension.Equals("ignore", StringComparison.InvariantCultureIgnoreCase)
197 StatusKeeper.ProcessExistingFiles(files);
200 catch (Exception exc)
202 Log.Error("[ERROR]", exc);
215 private void StartWorkflowAgent()
218 bool connected = NetworkListManager.IsConnectedToInternet;
219 //If we are not connected retry later
222 Task.Factory.StartNewDelayed(10000, StartWorkflowAgent);
228 WorkflowAgent.StatusNotification = StatusNotification;
229 WorkflowAgent.Start();
233 //Faild to authenticate due to network or account error
234 //Retry after a while
235 Task.Factory.StartNewDelayed(10000, StartWorkflowAgent);
239 public bool UsePithos { get; set; }
242 internal class LocalFileComparer:EqualityComparer<CloudAction>
244 public override bool Equals(CloudAction x, CloudAction y)
246 if (x.Action != y.Action)
248 if (x.LocalFile != null && y.LocalFile != null && !x.LocalFile.FullName.Equals(y.LocalFile.FullName))
250 if (x.CloudFile != null && y.CloudFile != null )
252 if (x.CloudFile.Hash == null & y.CloudFile.Hash != null)
254 if (x.CloudFile.Hash != null & y.CloudFile.Hash == null)
256 if (x.CloudFile.Hash == null & y.CloudFile.Hash == null)
257 return (x.CloudFile.Name == y.CloudFile.Name);
258 if (!x.CloudFile.Hash.Equals(y.CloudFile.Hash))
261 if (x.CloudFile == null ^ y.CloudFile == null ||
262 x.LocalFile == null ^ y.LocalFile == null)
267 public override int GetHashCode(CloudAction obj)
269 var hash1 = (obj.LocalFile == null) ? int.MaxValue : obj.LocalFile.FullName.GetHashCode();
270 var hash2 = (obj.CloudFile == null) ? int.MaxValue : (obj.CloudFile.Hash ?? obj.CloudFile.Name??"").GetHashCode();
271 var hash3 = obj.Action.GetHashCode();
272 return hash1 ^ hash2 & hash3;
278 private void StartNetworkAgent()
281 NetworkAgent.AddAccount(_accountInfo);
283 NetworkAgent.StatusNotification = StatusNotification;
285 NetworkAgent.Start();
287 NetworkAgent.ProcessRemoteFiles();
290 //Make sure a hidden fragments folder exists to store partial downloads
291 private static string CreateHiddenFolder(string rootPath, string folderName)
293 if (String.IsNullOrWhiteSpace(rootPath))
294 throw new ArgumentNullException("rootPath");
295 if (!Path.IsPathRooted(rootPath))
296 throw new ArgumentException("rootPath");
297 if (String.IsNullOrWhiteSpace(folderName))
298 throw new ArgumentNullException("folderName");
299 Contract.EndContractBlock();
301 var folder = Path.Combine(rootPath, folderName);
302 if (!Directory.Exists(folder))
304 var info = Directory.CreateDirectory(folder);
305 info.Attributes |= FileAttributes.Hidden;
307 Log.InfoFormat("Created Fragments Folder: {0}", folder);
315 private void StartWatcherAgent()
317 AgentLocator<FileAgent>.Register(FileAgent,RootPath);
319 FileAgent.StatusKeeper = StatusKeeper;
320 FileAgent.Workflow = Workflow;
321 FileAgent.FragmentsPath = Path.Combine(RootPath, FolderConstants.FragmentsFolder);
322 FileAgent.Start(_accountInfo, RootPath);
327 AgentLocator<FileAgent>.Remove(RootPath);
343 public void Dispose()
346 GC.SuppressFinalize(this);
349 protected virtual void Dispose(bool disposing)
358 public void MoveFileStates(string oldPath, string newPath)
360 if (String.IsNullOrWhiteSpace(oldPath))
361 throw new ArgumentNullException("oldPath");
362 if (!Path.IsPathRooted(oldPath))
363 throw new ArgumentException("oldPath must be an absolute path","oldPath");
364 if (string.IsNullOrWhiteSpace(newPath))
365 throw new ArgumentNullException("newPath");
366 if (!Path.IsPathRooted(newPath))
367 throw new ArgumentException("newPath must be an absolute path","newPath");
368 Contract.EndContractBlock();
370 StatusKeeper.ChangeRoots(oldPath, newPath);
373 public void AddSelectivePaths(string[] added)
375 /* FileAgent.SelectivePaths.AddRange(added);
376 NetworkAgent.SyncPaths(added);*/
379 public void RemoveSelectivePaths(string[] removed)
381 FileAgent.SelectivePaths.RemoveAll(removed.Contains);
382 foreach (var removedPath in removed.Where(Directory.Exists))
384 Directory.Delete(removedPath,true);
389 public interface IStatusNotification
391 void NotifyChange(string status,TraceLevel level=TraceLevel.Info);
392 void NotifyChangedFile(string filePath);