Fixes to the build process to detect the build environment's bitness
[pithos-ms-client] / trunk / Pithos.Client.WPF / Shell / ShellViewModel.cs
1 #region\r
2 /* -----------------------------------------------------------------------\r
3  * <copyright file="ShellViewModel.cs" company="GRNet">\r
4  * \r
5  * Copyright 2011-2012 GRNET S.A. All rights reserved.\r
6  *\r
7  * Redistribution and use in source and binary forms, with or\r
8  * without modification, are permitted provided that the following\r
9  * conditions are met:\r
10  *\r
11  *   1. Redistributions of source code must retain the above\r
12  *      copyright notice, this list of conditions and the following\r
13  *      disclaimer.\r
14  *\r
15  *   2. Redistributions in binary form must reproduce the above\r
16  *      copyright notice, this list of conditions and the following\r
17  *      disclaimer in the documentation and/or other materials\r
18  *      provided with the distribution.\r
19  *\r
20  *\r
21  * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS\r
22  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
23  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r
24  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR\r
25  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF\r
28  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\r
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\r
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
32  * POSSIBILITY OF SUCH DAMAGE.\r
33  *\r
34  * The views and conclusions contained in the software and\r
35  * documentation are those of the authors and should not be\r
36  * interpreted as representing official policies, either expressed\r
37  * or implied, of GRNET S.A.\r
38  * </copyright>\r
39  * -----------------------------------------------------------------------\r
40  */\r
41 #endregion\r
42 using System.Collections.Concurrent;\r
43 using System.Diagnostics;\r
44 using System.Diagnostics.Contracts;\r
45 using System.IO;\r
46 using System.Net;\r
47 using System.Reflection;\r
48 using System.Runtime.InteropServices;\r
49 using System.ServiceModel;\r
50 using System.Threading;\r
51 using System.Threading.Tasks;\r
52 using System.Windows;\r
53 using System.Windows.Controls.Primitives;\r
54 using System.Windows.Input;\r
55 using AppLimit.NetSparkle;\r
56 using Caliburn.Micro;\r
57 using Hardcodet.Wpf.TaskbarNotification;\r
58 using Pithos.Client.WPF.Configuration;\r
59 using Pithos.Client.WPF.FileProperties;\r
60 using Pithos.Client.WPF.Preferences;\r
61 using Pithos.Client.WPF.SelectiveSynch;\r
62 using Pithos.Client.WPF.Services;\r
63 using Pithos.Client.WPF.Shell;\r
64 using Pithos.Core;\r
65 using Pithos.Core.Agents;\r
66 using Pithos.Interfaces;\r
67 using System;\r
68 using System.Collections.Generic;\r
69 using System.Linq;\r
70 using Pithos.Network;\r
71 using StatusService = Pithos.Client.WPF.Services.StatusService;\r
72 \r
73 namespace Pithos.Client.WPF {\r
74         using System.ComponentModel.Composition;\r
75 \r
76         public class ToggleStatusCommand:ICommand\r
77         {\r
78             private readonly ShellViewModel _model;\r
79             public ToggleStatusCommand(ShellViewModel model)\r
80             {\r
81                 _model = model;\r
82             }\r
83             public void Execute(object parameter)\r
84             {\r
85                 _model.CurrentSyncStatus();\r
86             }\r
87 \r
88             public bool CanExecute(object parameter)\r
89             {\r
90                 return true;\r
91             }\r
92 \r
93             public event EventHandler CanExecuteChanged;\r
94         }\r
95 \r
96 \r
97         ///<summary>\r
98         /// The "shell" of the Pithos application displays the taskbar  icon, menu and notifications.\r
99         /// The shell also hosts the status service called by shell extensions to retrieve file info\r
100         ///</summary>\r
101         ///<remarks>\r
102         /// It is a strange "shell" as its main visible element is an icon instead of a window\r
103         /// The shell subscribes to the following events:\r
104         /// * Notification:  Raised by components that want to notify the user. Usually displayed in a balloon\r
105         /// * SelectiveSynchChanges: Notifies that the user made changes to the selective synch folders for an account. Raised by the Selective Synch dialog. Located here because the monitors are here\r
106         /// * ShowFilePropertiesEvent: Raised when a shell command requests the display of the file/container properties dialog\r
107         ///</remarks>           \r
108         //TODO: CODE SMELL Why does the shell handle the SelectiveSynchChanges?\r
109     [Export(typeof(IShell)), Export(typeof(ShellViewModel)),Export(typeof(IStatusNotification))]\r
110         public class ShellViewModel : Screen, IStatusNotification, IShell,\r
111                 IHandle<Notification>, IHandle<SelectiveSynchChanges>, IHandle<ShowFilePropertiesEvent>\r
112         {\r
113                 \r
114                 private readonly IEventAggregator _events;\r
115 \r
116                 public PithosSettings Settings { get; private set; }\r
117 \r
118 \r
119                 private readonly ConcurrentDictionary<Uri, PithosMonitor> _monitors = new ConcurrentDictionary<Uri, PithosMonitor>();\r
120                 ///<summary>\r
121                 /// Dictionary of account monitors, keyed by account\r
122                 ///</summary>\r
123                 ///<remarks>\r
124                 /// One monitor class is created for each account. The Shell needs access to the monitors to execute start/stop/pause commands,\r
125                 /// retrieve account and boject info            \r
126                 ///</remarks>\r
127                 // TODO: Does the Shell REALLY need access to the monitors? Could we achieve the same results with a better design?\r
128                 // TODO: The monitors should be internal to Pithos.Core, even though exposing them makes coding of the Object and Container windows easier\r
129                 public ConcurrentDictionary<Uri, PithosMonitor> Monitors\r
130                 {\r
131                         get { return _monitors; }\r
132                 }\r
133 \r
134 \r
135                 ///<summary>\r
136                 /// The status service is used by Shell extensions to retrieve file status information\r
137                 ///</summary>\r
138                 //TODO: CODE SMELL! This is the shell! While hosting in the shell makes executing start/stop commands easier, it is still a smell\r
139                 private ServiceHost _statusService;\r
140 \r
141                 //Logging in the Pithos client is provided by log4net\r
142         private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);\r
143 \r
144         #pragma warning disable 649\r
145         [Import]\r
146             private PollAgent _pollAgent;\r
147 \r
148         [Import]\r
149             private NetworkAgent _networkAgent;\r
150 \r
151             [Import]\r
152             public Selectives Selectives { get; set; }\r
153 \r
154         #pragma warning restore 649\r
155 \r
156         public ToggleStatusCommand ToggleMiniStatusCommand { get; set; }\r
157 \r
158             private MiniStatusViewModel _miniStatus;\r
159 \r
160             [Import]\r
161         public MiniStatusViewModel MiniStatus\r
162             {\r
163                 get { return _miniStatus; }\r
164                 set\r
165                 {\r
166                     _miniStatus = value;\r
167                     _miniStatus.Shell = this;\r
168                     _miniStatus.Deactivated += (sender, arg) =>\r
169                                                    {\r
170                                                        _statusVisible = false;\r
171                                                    NotifyOfPropertyChange(()=>MiniStatusCaption);\r
172                                                    };\r
173                 }\r
174             }\r
175 \r
176             ///<summary>\r
177                 /// The Shell depends on MEF to provide implementations for windowManager, events, the status checker service and the settings\r
178                 ///</summary>\r
179                 ///<remarks>\r
180                 /// The PithosSettings class encapsulates the app's settings to abstract their storage mechanism (App settings, a database or registry)\r
181                 ///</remarks>\r
182                 [ImportingConstructor]          \r
183                 public ShellViewModel(IWindowManager windowManager, IEventAggregator events, PithosSettings settings/*,PollAgent pollAgent,NetworkAgent networkAgent*/)\r
184                 {\r
185                         try\r
186                         {\r
187 \r
188                                 _windowManager = windowManager;\r
189                                 //CHECK: Caliburn doesn't need explicit command construction\r
190                                 //CurrentSyncStatusCommand = new PithosCommand(OpenPithosFolder);\r
191                                 //The event subst\r
192                                 _events = events;\r
193                                 _events.Subscribe(this);\r
194 \r
195 /*\r
196                             _pollAgent = pollAgent;\r
197                             _networkAgent = networkAgent;\r
198 */\r
199                                 Settings = settings;\r
200 \r
201                                 Proxy.SetFromSettings(settings);\r
202 \r
203                 StatusMessage = Settings.Accounts.Count==0 \r
204                     ? "No Accounts added\r\nPlease add an account" \r
205                     : "Starting";\r
206 \r
207                                 _accounts.CollectionChanged += (sender, e) =>\r
208                                                                                                    {\r
209                                                                                                            NotifyOfPropertyChange(() => OpenFolderCaption);\r
210                                                                                                            NotifyOfPropertyChange(() => HasAccounts);\r
211                                                                                                    };\r
212 \r
213                 SetVersionMessage();\r
214 \r
215                 ToggleMiniStatusCommand=new ToggleStatusCommand(this);\r
216                         }\r
217                         catch (Exception exc)\r
218                         {\r
219                                 Log.Error("Error while starting the ShellViewModel",exc);\r
220                                 throw;\r
221                         }\r
222 \r
223                 }\r
224 \r
225             private void SetVersionMessage()\r
226             {\r
227                 Assembly assembly = Assembly.GetExecutingAssembly();\r
228                 var fileVersion = FileVersionInfo.GetVersionInfo(assembly.Location);\r
229                 VersionMessage = String.Format("Pithos+ {0}", fileVersion.FileVersion);\r
230             }\r
231 \r
232         public void CurrentSyncStatus()\r
233         {\r
234             if (Accounts.Count == 0)\r
235             {\r
236                 ShowPreferences("AccountTab");\r
237             }\r
238             else\r
239             {\r
240                 if (!_statusVisible)\r
241                 {\r
242                     _windowManager.ShowWindow(MiniStatus);\r
243                     _statusVisible = true;\r
244                 }\r
245                 else\r
246                 {\r
247                     if (MiniStatus.IsActive)\r
248                         MiniStatus.TryClose();\r
249                     _statusVisible = false;\r
250                 }\r
251 \r
252                 NotifyOfPropertyChange(() => MiniStatusCaption);\r
253             }\r
254         }\r
255 \r
256             protected override void OnActivate()\r
257                 {\r
258                         base.OnActivate();\r
259 \r
260             InitializeSparkle();\r
261 \r
262                 //Must delay opening the upgrade window\r
263             //to avoid Windows Messages sent by the TaskbarIcon\r
264             TaskEx.Delay(5000).ContinueWith(_=>\r
265                 Execute.OnUIThread(()=> _sparkle.StartLoop(true,Settings.UpdateForceCheck,Settings.UpdateCheckInterval)));\r
266 \r
267 \r
268                         StartMonitoring();                    \r
269                 }\r
270 \r
271 \r
272             private void OnCheckFinished(object sender, bool updaterequired)\r
273             {\r
274             \r
275             Log.InfoFormat("Upgrade check finished. Need Upgrade: {0}", updaterequired);\r
276             if (_manualUpgradeCheck)\r
277             {\r
278                 _manualUpgradeCheck = false;\r
279                 if (!updaterequired)\r
280                     //Sparkle raises events on a background thread\r
281                     Execute.OnUIThread(()=>\r
282                         ShowBalloonFor(new Notification{Title="Pithos+ is up to date",Message="You have the latest Pithos+ version. No update is required"}));\r
283             }\r
284             }\r
285 \r
286             private void OnUpgradeDetected(object sender, UpdateDetectedEventArgs e)\r
287             {            \r
288                 Log.InfoFormat("Update detected {0}",e.LatestVersion);\r
289             }\r
290 \r
291         public void CheckForUpgrade()\r
292         {\r
293             ShowBalloonFor(new Notification{Title="Checking for upgrades",Message="Contacting the server to retrieve the latest Pithos+ version."});\r
294             _sparkle.StopLoop();\r
295             _sparkle.updateDetected -= OnUpgradeDetected;\r
296             _sparkle.checkLoopFinished -= OnCheckFinished;\r
297             _sparkle.Dispose();\r
298 \r
299             _manualUpgradeCheck = true;\r
300             InitializeSparkle();\r
301             _sparkle.StartLoop(true,true,Settings.UpdateCheckInterval);\r
302         }\r
303 \r
304         private void InitializeSparkle()\r
305         {\r
306             _sparkle = new Sparkle(Settings.UpdateUrl);\r
307             _sparkle.updateDetected += OnUpgradeDetected;\r
308             _sparkle.checkLoopFinished += OnCheckFinished;\r
309             _sparkle.ShowDiagnosticWindow = Settings.UpdateDiagnostics;\r
310         }\r
311 \r
312             private async void StartMonitoring()\r
313                 {\r
314                         try\r
315                         {\r
316                 if (Settings.IgnoreCertificateErrors)\r
317                 {\r
318                     ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true;\r
319                 }\r
320                     \r
321                                 var accounts = Settings.Accounts.Select(MonitorAccount);\r
322                 await TaskEx.WhenAll(accounts).ConfigureAwait(false);\r
323                                 _statusService = StatusService.Start();\r
324 \r
325                         }\r
326                         catch (AggregateException exc)\r
327                         {\r
328                                 exc.Handle(e =>\r
329                                 {\r
330                                         Log.Error("Error while starting monitoring", e);\r
331                                         return true;\r
332                                 });\r
333                                 throw;\r
334                         }\r
335                 }\r
336 \r
337                 protected override void OnDeactivate(bool close)\r
338                 {\r
339                         base.OnDeactivate(close);\r
340                         if (close)\r
341                         {\r
342                                 StatusService.Stop(_statusService);\r
343                                 _statusService = null;\r
344                         }\r
345                 }\r
346 \r
347                 public Task MonitorAccount(AccountSettings account)\r
348                 {\r
349                         return Task.Factory.StartNew(() =>\r
350                         {                                                \r
351                                 PithosMonitor monitor;\r
352                                 var accountName = account.AccountName;\r
353 \r
354                             MigrateFolders(account);\r
355 \r
356                             Selectives.SetIsSelectiveEnabled(account.AccountKey, account.SelectiveSyncEnabled);\r
357 \r
358                                 if (Monitors.TryGetValue(account.AccountKey, out monitor))\r
359                                 {\r
360                                         //If the account is active\r
361                     if (account.IsActive)\r
362                     {\r
363                         //The Api Key may have changed throuth the Preferences dialog\r
364                         monitor.ApiKey = account.ApiKey;\r
365                                                 Debug.Assert(monitor.StatusNotification == this,"An existing monitor should already have a StatusNotification service object");\r
366                         monitor.RootPath = account.RootPath;\r
367                         //Start the monitor. It's OK to start an already started monitor,\r
368                         //it will just ignore the call                        \r
369                         StartMonitor(monitor).Wait();\r
370                     }\r
371                     else\r
372                     {\r
373                         //If the account is inactive\r
374                         //Stop and remove the monitor\r
375                         RemoveMonitor(account.ServerUrl,accountName);\r
376                     }\r
377                                         return;\r
378                                 }\r
379 \r
380                                 \r
381                                 //Create a new monitor/ Can't use MEF here, it would return a single instance for all monitors\r
382                                 monitor = new PithosMonitor\r
383                                                           {\r
384                                                                   UserName = accountName,\r
385                                                                   ApiKey = account.ApiKey,                                  \r
386                                                                   StatusNotification = this,\r
387                                                                   RootPath = account.RootPath\r
388                                                           };\r
389                                 //PithosMonitor uses MEF so we need to resolve it\r
390                                 IoC.BuildUp(monitor);\r
391 \r
392                                 monitor.AuthenticationUrl = account.ServerUrl;\r
393 \r
394                                 Monitors[account.AccountKey] = monitor;\r
395 \r
396                 if (!Directory.Exists(account.RootPath))\r
397                 {\r
398                     account.IsActive = false;\r
399                     Settings.Save();\r
400                     Notify(new Notification\r
401                     {\r
402                         Level = TraceLevel.Error,\r
403                         Title = "Missing account folder",\r
404                         Message = String.Format("Can't find the root folder for account {0} at {1}. The account was deactivated.\r" +\r
405                         "If the account's files were stored in a removable disk, please connect it and reactivate the account", account.AccountName, account.RootPath)\r
406                     });\r
407                 }\r
408 \r
409 \r
410                                 if (account.IsActive)\r
411                                 {\r
412                                         //Don't start a monitor if it doesn't have an account and ApiKey\r
413                                         if (String.IsNullOrWhiteSpace(monitor.UserName) ||\r
414                                                 String.IsNullOrWhiteSpace(monitor.ApiKey))\r
415                                                 return;\r
416                                         StartMonitor(monitor);\r
417                                 }\r
418                         });\r
419                 }\r
420 \r
421             private void MigrateFolders(AccountSettings account)\r
422             {\r
423                 var oldOthersFolder=Path.Combine(account.RootPath, FolderConstants.OldOthersFolder);\r
424                 var newOthersFolder = Path.Combine(account.RootPath, FolderConstants.OthersFolder);\r
425                 var oldFolder = new DirectoryInfo(oldOthersFolder);\r
426                 var newFolder = new DirectoryInfo(newOthersFolder);\r
427 \r
428             if (oldFolder.Exists && !newFolder.Exists)\r
429             {\r
430                 oldFolder.MoveTo(newOthersFolder);\r
431             }\r
432             }\r
433 \r
434 \r
435             protected override void OnViewLoaded(object view)\r
436                 {\r
437                         UpdateStatus();\r
438                         var window = (Window)view;            \r
439                         TaskEx.Delay(1000).ContinueWith(t => Execute.OnUIThread(window.Hide));\r
440                         base.OnViewLoaded(view);\r
441                 }\r
442 \r
443 \r
444                 #region Status Properties\r
445 \r
446                 private string _statusMessage;\r
447                 public string StatusMessage\r
448                 {\r
449                         get { return _statusMessage; }\r
450                         set\r
451                         {\r
452                                 _statusMessage = value;\r
453                                 NotifyOfPropertyChange(() => StatusMessage);\r
454                 NotifyOfPropertyChange(() => TooltipMessage);\r
455                         }\r
456                 }\r
457 \r
458         public string VersionMessage { get; set; }\r
459 \r
460             public string TooltipMessage\r
461             {\r
462                 get\r
463                 {\r
464                     return String.Format("{0}\r\n{1}",VersionMessage,StatusMessage);\r
465                 }\r
466             }\r
467 \r
468         public string TooltipMiniStatus\r
469         {\r
470             get\r
471             {\r
472                 return String.Format("{0}\r\n{1}", "Status Window", "Enable / Disable the status window");\r
473             }\r
474         }\r
475 \r
476         /*public string ToggleStatusWindowMessage\r
477         {\r
478             get\r
479             {\r
480                 return String.Format("{0}" + Environment.NewLine + "{1} Toggle Mini Status");\r
481             }\r
482         }*/\r
483 \r
484             private readonly ObservableConcurrentCollection<AccountInfo> _accounts = new ObservableConcurrentCollection<AccountInfo>();\r
485                 public ObservableConcurrentCollection<AccountInfo> Accounts\r
486                 {\r
487                         get { return _accounts; }\r
488                 }\r
489 \r
490                 public bool HasAccounts\r
491                 {\r
492                         get { return _accounts.Count > 0; }\r
493                 }\r
494 \r
495 \r
496                 public string OpenFolderCaption\r
497                 {\r
498                         get\r
499                         {\r
500                                 return (_accounts.Count == 0)\r
501                                                 ? "No Accounts Defined"\r
502                                                 : "Open Pithos Folder";\r
503                         }\r
504                 }\r
505 \r
506                 private string _pauseSyncCaption="Pause Synching";\r
507                 public string PauseSyncCaption\r
508                 {\r
509                         get { return _pauseSyncCaption; }\r
510                         set\r
511                         {\r
512                                 _pauseSyncCaption = value;\r
513                                 NotifyOfPropertyChange(() => PauseSyncCaption);\r
514                         }\r
515                 }\r
516 \r
517                 private readonly ObservableConcurrentCollection<FileEntry> _recentFiles = new ObservableConcurrentCollection<FileEntry>();\r
518                 public ObservableConcurrentCollection<FileEntry> RecentFiles\r
519                 {\r
520                         get { return _recentFiles; }\r
521                 }\r
522 \r
523 \r
524                 private string _statusIcon="../Images/Pithos.ico";\r
525                 public string StatusIcon\r
526                 {\r
527                         get { return _statusIcon; }\r
528                         set\r
529                         {\r
530                                 //TODO: Ensure all status icons use the Pithos logo\r
531                                 _statusIcon = value;\r
532                                 NotifyOfPropertyChange(() => StatusIcon);\r
533                         }\r
534                 }\r
535 \r
536                 #endregion\r
537 \r
538                 #region Commands\r
539 \r
540         public void CancelCurrentOperation()\r
541         {\r
542             _pollAgent.CancelCurrentOperation();\r
543         }\r
544 \r
545         public void ShowPreferences()\r
546         {\r
547             ShowPreferences(null);\r
548         }\r
549 \r
550                 public void ShowPreferences(string currentTab)\r
551                 {\r
552                         //Settings.Reload();\r
553             \r
554                     var preferences = IoC.Get<PreferencesViewModel>();//??new PreferencesViewModel(_windowManager, _events, this, Settings,currentTab);\r
555             if (!String.IsNullOrWhiteSpace(currentTab))\r
556                 preferences.SelectedTab = currentTab;\r
557             if (!preferences.IsActive)\r
558                         _windowManager.ShowWindow(preferences);\r
559             var view = (Window)preferences.GetView();\r
560             view.NullSafe(v=>v.Activate());\r
561                 }\r
562 \r
563                 public void AboutPithos()\r
564                 {\r
565                         var about = IoC.Get<AboutViewModel>();\r
566                     about.LatestVersion=_sparkle.LatestVersion;\r
567                         _windowManager.ShowWindow(about);\r
568                 }\r
569 \r
570                 public void SendFeedback()\r
571                 {\r
572                         var feedBack =  IoC.Get<FeedbackViewModel>();\r
573                         _windowManager.ShowWindow(feedBack);\r
574                 }\r
575 \r
576                 //public PithosCommand OpenPithosFolderCommand { get; private set; }\r
577 \r
578                 public void OpenPithosFolder()\r
579                 {\r
580                         var account = Settings.Accounts.FirstOrDefault(acc => acc.IsActive);\r
581                         if (account == null)\r
582                                 return;\r
583                         Process.Start(account.RootPath);\r
584                 }\r
585 \r
586                 public void OpenPithosFolder(AccountInfo account)\r
587                 {\r
588                         Process.Start(account.AccountPath);\r
589                 }\r
590 \r
591                 \r
592 \r
593                 public void GoToSite()\r
594                 {            \r
595                         var site = Properties.Settings.Default.ProductionServer;\r
596                         Process.Start(site);            \r
597                 }\r
598 \r
599 \r
600                 public void GoToSite(AccountInfo account)\r
601                 {\r
602                     var uri = account.SiteUri.Replace("http://","https://");            \r
603                     Process.Start(uri);\r
604                 }\r
605 \r
606             private bool _statusVisible;\r
607 \r
608             public string MiniStatusCaption\r
609             {\r
610                 get\r
611                 {\r
612                     return  _statusVisible ? "Hide Status Window" : "Show Status Window";\r
613                 }\r
614             }\r
615 \r
616             public bool HasConflicts\r
617             {\r
618             get { return true; }\r
619             }\r
620         public void ShowConflicts()\r
621         {\r
622             _windowManager.ShowWindow(IoC.Get<ConflictsViewModel>());            \r
623         }\r
624 \r
625             /// <summary>\r
626         /// Open an explorer window to the target path's directory\r
627         /// and select the file\r
628         /// </summary>\r
629         /// <param name="entry"></param>\r
630         public void GoToFile(FileEntry entry)\r
631         {\r
632             var fullPath = entry.FullPath;\r
633             if (!File.Exists(fullPath) && !Directory.Exists(fullPath))\r
634                 return;\r
635             Process.Start("explorer.exe","/select, " + fullPath);\r
636         }\r
637 \r
638         public void OpenLogPath()\r
639         {\r
640             var pithosDataPath = PithosSettings.PithosDataPath;\r
641 \r
642             Process.Start(pithosDataPath);\r
643         }\r
644         \r
645         public void ShowFileProperties()\r
646                 {\r
647                         var account = Settings.Accounts.First(acc => acc.IsActive);            \r
648                         var dir = new DirectoryInfo(account.RootPath + @"\pithos");\r
649                         var files=dir.GetFiles();\r
650                         var r=new Random();\r
651                         var idx=r.Next(0, files.Length);\r
652                         ShowFileProperties(files[idx].FullName);            \r
653                 }\r
654 \r
655                 public void ShowFileProperties(string filePath)\r
656                 {\r
657                         if (String.IsNullOrWhiteSpace(filePath))\r
658                                 throw new ArgumentNullException("filePath");\r
659                         if (!File.Exists(filePath) && !Directory.Exists(filePath))\r
660                                 throw new ArgumentException(String.Format("Non existent file {0}",filePath),"filePath");\r
661                         Contract.EndContractBlock();\r
662 \r
663                         var pair=(from monitor in  Monitors\r
664                                                            where filePath.StartsWith(monitor.Value.RootPath, StringComparison.InvariantCultureIgnoreCase)\r
665                                                                    select monitor).FirstOrDefault();\r
666                         var accountMonitor = pair.Value;\r
667 \r
668                         if (accountMonitor == null)\r
669                                 return;\r
670 \r
671                         var infoTask=accountMonitor.GetObjectInfo(filePath);\r
672 \r
673                         \r
674 \r
675                         var fileProperties = new FilePropertiesViewModel(this, infoTask,filePath);\r
676                         _windowManager.ShowWindow(fileProperties);\r
677                 } \r
678                 \r
679                 public void ShowContainerProperties()\r
680                 {\r
681                         var account = Settings.Accounts.First(acc => acc.IsActive);            \r
682                         var dir = new DirectoryInfo(account.RootPath);\r
683                         var fullName = (from folder in dir.EnumerateDirectories()\r
684                                                         where (folder.Attributes & FileAttributes.Hidden) == 0\r
685                                                         select folder.FullName).First();\r
686                         ShowContainerProperties(fullName);            \r
687                 }\r
688 \r
689                 public void ShowContainerProperties(string filePath)\r
690                 {\r
691                         if (String.IsNullOrWhiteSpace(filePath))\r
692                                 throw new ArgumentNullException("filePath");\r
693                         if (!Directory.Exists(filePath))\r
694                                 throw new ArgumentException(String.Format("Non existent file {0}",filePath),"filePath");\r
695                         Contract.EndContractBlock();\r
696 \r
697                         var pair=(from monitor in  Monitors\r
698                                                            where filePath.StartsWith(monitor.Value.RootPath, StringComparison.InvariantCultureIgnoreCase)\r
699                                                                    select monitor).FirstOrDefault();\r
700                         var accountMonitor = pair.Value;            \r
701                         var info = accountMonitor.GetContainerInfo(filePath);\r
702 \r
703                         \r
704 \r
705                         var containerProperties = new ContainerPropertiesViewModel(this, info,filePath);\r
706                         _windowManager.ShowWindow(containerProperties);\r
707                 }\r
708 \r
709                 public void SynchNow()\r
710                 {\r
711                         _pollAgent.SynchNow();\r
712                 }\r
713 \r
714                 public async Task<ObjectInfo> RefreshObjectInfo(ObjectInfo currentInfo)\r
715                 {\r
716                         if (currentInfo==null)\r
717                                 throw new ArgumentNullException("currentInfo");\r
718                         Contract.EndContractBlock();                \r
719             var monitor = Monitors[currentInfo.AccountKey];\r
720                         var newInfo=await monitor.CloudClient.GetObjectInfo(currentInfo.Account, currentInfo.Container, currentInfo.Name).ConfigureAwait(false);\r
721                         return newInfo;\r
722                 }\r
723 \r
724                 public async Task<ContainerInfo> RefreshContainerInfo(ContainerInfo container)\r
725                 {\r
726                         if (container == null)\r
727                                 throw new ArgumentNullException("container");\r
728                         Contract.EndContractBlock();\r
729 \r
730                         var monitor = Monitors[container.AccountKey];\r
731                         var newInfo = await monitor.CloudClient.GetContainerInfo(container.Account, container.Name).ConfigureAwait(false);\r
732                         return newInfo;\r
733                 }\r
734 \r
735             private bool _isPaused;\r
736             public bool IsPaused\r
737             {\r
738                 get { return _isPaused; }\r
739                 set\r
740                 {\r
741                     _isPaused = value;\r
742                 PauseSyncCaption = IsPaused ? "Resume syncing" : "Pause syncing";\r
743                 var iconKey = IsPaused ? "TraySyncPaused" : "TrayInSynch";\r
744                 StatusIcon = String.Format(@"../Images/{0}.ico", iconKey);\r
745 \r
746                 NotifyOfPropertyChange(() => IsPaused);\r
747                 }\r
748             }\r
749 \r
750             public void ToggleSynching()\r
751                 {\r
752                         IsPaused=!IsPaused;\r
753                         foreach (var monitor in Monitors.Values)\r
754                         {\r
755                             monitor.Pause = IsPaused ;\r
756                         }\r
757             _pollAgent.Pause = IsPaused;\r
758             _networkAgent.Pause = IsPaused;\r
759 \r
760 \r
761                 }\r
762 \r
763         public void ExitPithos()\r
764         {\r
765             try\r
766             {\r
767 \r
768                 foreach (var monitor in Monitors.Select(pair => pair.Value))\r
769                 {\r
770                     monitor.Stop();\r
771                 }\r
772 \r
773                 var view = GetView() as Window;\r
774                 if (view != null)\r
775                     view.Close();\r
776             }\r
777             catch (Exception exc)\r
778             {\r
779                 Log.Info("Exception while exiting", exc);                \r
780             }\r
781             finally\r
782             {\r
783                 Application.Current.Shutdown();\r
784             }\r
785         }\r
786 \r
787             #endregion\r
788 \r
789 \r
790                 private readonly Dictionary<PithosStatus, StatusInfo> _iconNames = new List<StatusInfo>\r
791                         {\r
792                                 new StatusInfo(PithosStatus.InSynch, "All files up to date", "TrayInSynch"),\r
793                                 new StatusInfo(PithosStatus.PollSyncing, "Polling Files", "TraySynching"),\r
794                 new StatusInfo(PithosStatus.LocalSyncing, "Syncing Files", "TraySynching"),\r
795                                 new StatusInfo(PithosStatus.SyncPaused, "Sync Paused", "TraySyncPaused")\r
796                         }.ToDictionary(s => s.Status);\r
797 \r
798                 readonly IWindowManager _windowManager;\r
799                 \r
800         //private int _syncCount=0;\r
801 \r
802 \r
803         private PithosStatus _pithosStatus = PithosStatus.Disconnected;\r
804 \r
805         public void SetPithosStatus(PithosStatus status)\r
806         {\r
807             if (_pithosStatus == PithosStatus.LocalSyncing && status == PithosStatus.PollComplete)\r
808                 return;\r
809             if (_pithosStatus == PithosStatus.PollSyncing && status == PithosStatus.LocalComplete)\r
810                 return;\r
811             if (status == PithosStatus.LocalComplete || status == PithosStatus.PollComplete)\r
812                 _pithosStatus = PithosStatus.InSynch;\r
813             else\r
814                 _pithosStatus = status;\r
815             UpdateStatus();\r
816         }\r
817 \r
818         public void SetPithosStatus(PithosStatus status,string message)\r
819         {\r
820             StatusMessage = message;\r
821             SetPithosStatus(status);\r
822         }\r
823 \r
824           /*  public Notifier GetNotifier(Notification startNotification, Notification endNotification)\r
825             {\r
826                 return new Notifier(this, startNotification, endNotification);\r
827             }*/\r
828 \r
829             public Notifier GetNotifier(string startMessage, string endMessage, bool isActive=true,params object[] args)\r
830             {\r
831                 return isActive?new Notifier(this, \r
832                 new StatusNotification(String.Format(startMessage,args)), \r
833                 new StatusNotification(String.Format(endMessage,args)))\r
834                 :new Notifier(this,(Notification) null,null);\r
835             }\r
836 \r
837 \r
838             ///<summary>\r
839                 /// Updates the visual status indicators of the application depending on status changes, e.g. icon, stat                \r
840                 ///</summary>\r
841                 public void UpdateStatus()\r
842                 {\r
843 \r
844                         if (_iconNames.ContainsKey(_pithosStatus))\r
845                         {\r
846                                 var info = _iconNames[_pithosStatus];\r
847                                 StatusIcon = String.Format(@"../Images/{0}.ico", info.IconName);\r
848                         }\r
849 \r
850             if (_pithosStatus == PithosStatus.InSynch)\r
851                 StatusMessage = "All files up to date";\r
852                 }\r
853 \r
854 \r
855            \r
856                 private Task StartMonitor(PithosMonitor monitor,int retries=0)\r
857                 {\r
858                         return Task.Factory.StartNew(() =>\r
859                         {\r
860                                 using (log4net.ThreadContext.Stacks["Monitor"].Push("Start"))\r
861                                 {\r
862                                         try\r
863                                         {\r
864                                                 Log.InfoFormat("Start Monitoring {0}", monitor.UserName);\r
865 \r
866                                                 monitor.Start();\r
867                                         }\r
868                                         catch (WebException exc)\r
869                                         {\r
870                                                 if (AbandonRetry(monitor, retries))\r
871                                                         return;\r
872 \r
873                                                 HttpStatusCode statusCode =HttpStatusCode.OK;\r
874                                                 var response = exc.Response as HttpWebResponse;\r
875                                                 if(response!=null)\r
876                                                         statusCode = response.StatusCode;\r
877 \r
878                                                 switch (statusCode)\r
879                                                 {\r
880                                                         case HttpStatusCode.Unauthorized:\r
881                                                                 var message = String.Format("API Key Expired for {0}. Starting Renewal",\r
882                                                                                                                         monitor.UserName);\r
883                                                                 Log.Error(message, exc);\r
884                                 var account = Settings.Accounts.Find(acc => acc.AccountKey == new Uri(monitor.AuthenticationUrl).Combine(monitor.UserName));                                \r
885                                                         account.IsExpired = true;\r
886                                 Notify(new ExpirationNotification(account));\r
887                                                                 //TryAuthorize(monitor.UserName, retries).Wait();\r
888                                                                 break;\r
889                                                         case HttpStatusCode.ProxyAuthenticationRequired:\r
890                                                                 TryAuthenticateProxy(monitor,retries);\r
891                                                                 break;\r
892                                                         default:\r
893                                                                 TryLater(monitor, exc, retries);\r
894                                                                 break;\r
895                                                 }\r
896                                         }\r
897                                         catch (Exception exc)\r
898                                         {\r
899                                                 if (AbandonRetry(monitor, retries)) \r
900                                                         return;\r
901 \r
902                                                 TryLater(monitor,exc,retries);\r
903                                         }\r
904                                 }\r
905                         });\r
906                 }\r
907 \r
908                 private void TryAuthenticateProxy(PithosMonitor monitor,int retries)\r
909                 {\r
910                         Execute.OnUIThread(() =>\r
911                                                                    {                                       \r
912                                                                            var proxyAccount = IoC.Get<ProxyAccountViewModel>();\r
913                                                                                 proxyAccount.Settings = Settings;\r
914                                                                            if (true != _windowManager.ShowDialog(proxyAccount)) \r
915                                                                                    return;\r
916                                                                            StartMonitor(monitor, retries);\r
917                                                                            NotifyOfPropertyChange(() => Accounts);\r
918                                                                    });\r
919                 }\r
920 \r
921                 private bool AbandonRetry(PithosMonitor monitor, int retries)\r
922                 {\r
923                         if (retries > 3)\r
924                         {\r
925                                 var message = String.Format("Monitoring of account {0} has failed too many times. Will not retry",\r
926                                                                                         monitor.UserName);\r
927                                 _events.Publish(new Notification\r
928                                                                         {Title = "Account monitoring failed", Message = message, Level = TraceLevel.Error});\r
929                                 return true;\r
930                         }\r
931                         return false;\r
932                 }\r
933 \r
934 \r
935             private void TryLater(PithosMonitor monitor, Exception exc,int retries)\r
936                 {\r
937                         var message = String.Format("An exception occured. Can't start monitoring\nWill retry in 10 seconds");\r
938                         Task.Factory.StartNewDelayed(10000, () => StartMonitor(monitor,retries+1));\r
939                         _events.Publish(new Notification\r
940                                                                 {Title = "Error", Message = message, Level = TraceLevel.Error});\r
941                         Log.Error(message, exc);\r
942                 }\r
943 \r
944 \r
945                 public void NotifyChange(string status, TraceLevel level=TraceLevel.Info)\r
946                 {\r
947                         StatusMessage = status;\r
948                         \r
949                         _events.Publish(new Notification { Title = "Pithos+", Message = status, Level = level });\r
950                 }\r
951 \r
952                 public void NotifyChangedFile(string filePath)\r
953                 {\r
954             if (RecentFiles.Any(e => e.FullPath == filePath))\r
955                 return;\r
956             \r
957                         IProducerConsumerCollection<FileEntry> files=RecentFiles;\r
958                         FileEntry popped;\r
959                         while (files.Count > 5)\r
960                                 files.TryTake(out popped);\r
961             var entry = new FileEntry { FullPath = filePath };\r
962                         files.TryAdd(entry);\r
963                 }\r
964 \r
965                 public void NotifyAccount(AccountInfo account)\r
966                 {\r
967                         if (account== null)\r
968                                 return;\r
969                         //TODO: What happens to an existing account whose Token has changed?\r
970                         account.SiteUri= String.Format("{0}/ui/?token={1}&user={2}",\r
971                                 account.SiteUri, Uri.EscapeDataString(account.Token),\r
972                                 Uri.EscapeDataString(account.UserName));\r
973 \r
974                         if (!Accounts.Any(item => item.UserName == account.UserName && item.SiteUri == account.SiteUri))\r
975                                 Accounts.TryAdd(account);\r
976 \r
977                 }\r
978 \r
979                 public void NotifyConflicts(IEnumerable<FileSystemInfo> conflictFiles, string message)\r
980                 {\r
981                         if (conflictFiles == null)\r
982                                 return;\r
983                     //Convert to list to avoid multiple iterations\r
984             var files = conflictFiles.ToList();\r
985                         if (files.Count==0)\r
986                                 return;\r
987 \r
988                         UpdateStatus();\r
989                         //TODO: Create a more specific message. For now, just show a warning\r
990                         NotifyForFiles(files,message,TraceLevel.Warning);\r
991 \r
992                 }\r
993 \r
994                 public void NotifyForFiles(IEnumerable<FileSystemInfo> files, string message,TraceLevel level=TraceLevel.Info)\r
995                 {\r
996                         if (files == null)\r
997                                 return;\r
998                         if (!files.Any())\r
999                                 return;\r
1000 \r
1001                         StatusMessage = message;\r
1002 \r
1003                         _events.Publish(new Notification { Title = "Pithos+", Message = message, Level = level});\r
1004                 }\r
1005 \r
1006                 public void Notify(Notification notification)\r
1007                 {\r
1008             TaskEx.Run(()=> _events.Publish(notification));\r
1009                 }\r
1010 \r
1011 \r
1012                 public void RemoveMonitor(string serverUrl,string accountName)\r
1013                 {\r
1014                         if (String.IsNullOrWhiteSpace(accountName))\r
1015                                 return;\r
1016 \r
1017                         var accountInfo=_accounts.FirstOrDefault(account => account.UserName == accountName && account.StorageUri.ToString().StartsWith(serverUrl));\r
1018             if (accountInfo != null)\r
1019             {\r
1020                 _accounts.TryRemove(accountInfo);\r
1021                 _pollAgent.RemoveAccount(accountInfo);\r
1022             }\r
1023 \r
1024             var accountKey = new Uri(serverUrl).Combine(accountName);\r
1025                     PithosMonitor monitor;\r
1026                         if (Monitors.TryRemove(accountKey, out monitor))\r
1027                         {\r
1028                                 monitor.Stop();\r
1029                 //TODO: Also remove any pending actions for this account\r
1030                 //from the network queue                \r
1031                         }\r
1032                 }\r
1033 \r
1034                 public void RefreshOverlays()\r
1035                 {\r
1036                         foreach (var pair in Monitors)\r
1037                         {\r
1038                                 var monitor = pair.Value;\r
1039 \r
1040                                 var path = monitor.RootPath;\r
1041 \r
1042                                 if (String.IsNullOrWhiteSpace(path))\r
1043                                         continue;\r
1044 \r
1045                                 if (!Directory.Exists(path) && !File.Exists(path))\r
1046                                         continue;\r
1047 \r
1048                                 IntPtr pathPointer = Marshal.StringToCoTaskMemAuto(path);\r
1049 \r
1050                                 try\r
1051                                 {\r
1052                                         NativeMethods.SHChangeNotify(HChangeNotifyEventID.SHCNE_UPDATEITEM,\r
1053                                                                                                  HChangeNotifyFlags.SHCNF_PATHW | HChangeNotifyFlags.SHCNF_FLUSHNOWAIT,\r
1054                                                                                                  pathPointer, IntPtr.Zero);\r
1055                                 }\r
1056                                 finally\r
1057                                 {\r
1058                                         Marshal.FreeHGlobal(pathPointer);\r
1059                                 }\r
1060                         }\r
1061                 }\r
1062 \r
1063                 #region Event Handlers\r
1064                 \r
1065                 public void Handle(SelectiveSynchChanges message)\r
1066                 {\r
1067                     TaskEx.Run(() =>\r
1068                     {\r
1069                         PithosMonitor monitor;\r
1070                         if (Monitors.TryGetValue(message.Account.AccountKey, out monitor))\r
1071                         {\r
1072                     Selectives.SetIsSelectiveEnabled(message.Account.AccountKey, message.Enabled);\r
1073                             monitor.SetSelectivePaths(message.Uris, message.Added, message.Removed);\r
1074                         }\r
1075 \r
1076                         var account = Accounts.FirstOrDefault(acc => acc.AccountKey == message.Account.AccountKey);\r
1077                         if (account != null)\r
1078                         {\r
1079                             var added=monitor.UrisToFilePaths(message.Added);\r
1080                     _pollAgent.SynchNow(added);\r
1081                         }\r
1082                     });\r
1083 \r
1084                 }\r
1085 \r
1086 \r
1087                 private bool _pollStarted;\r
1088             private Sparkle _sparkle;\r
1089             private bool _manualUpgradeCheck;\r
1090 \r
1091             //SMELL: Doing so much work for notifications in the shell is wrong\r
1092                 //The notifications should be moved to their own view/viewmodel pair\r
1093                 //and different templates should be used for different message types\r
1094                 //This will also allow the addition of extra functionality, eg. actions\r
1095                 //\r
1096                 public void Handle(Notification notification)\r
1097                 {\r
1098                         UpdateStatus();\r
1099 \r
1100                         if (!Settings.ShowDesktopNotifications)\r
1101                                 return;\r
1102 \r
1103                         if (notification is PollNotification)\r
1104                         {\r
1105                                 _pollStarted = true;\r
1106                                 return;\r
1107                         }\r
1108                         if (notification is CloudNotification)\r
1109                         {\r
1110                                 if (!_pollStarted) \r
1111                                         return;\r
1112                                 _pollStarted= false;\r
1113                                 notification.Title = "Pithos+";\r
1114                                 notification.Message = "Start Synchronisation";\r
1115                         }\r
1116 \r
1117                     var deleteNotification = notification as CloudDeleteNotification;\r
1118             if (deleteNotification != null)\r
1119             {\r
1120                 StatusMessage = String.Format("Deleted {0}", deleteNotification.Data.Name);\r
1121                 return;\r
1122             }\r
1123 \r
1124                     var progress = notification as ProgressNotification;\r
1125                     \r
1126                     \r
1127             if (progress != null)\r
1128             {\r
1129                 double percentage = (progress.TotalBlocks == progress.Block) ? 1\r
1130                     :(progress.Block + progress.BlockPercentage / 100.0) / (double)progress.TotalBlocks;\r
1131                         StatusMessage = String.Format("{0} {1:p2} of {2} - {3}",                                                      \r
1132                                               progress.Action,\r
1133                                                       percentage,\r
1134                                                       progress.FileSize.ToByteSize(),\r
1135                                                       progress.FileName);\r
1136                         return;\r
1137                     }\r
1138 \r
1139                     var info = notification as StatusNotification;\r
1140             if (info != null)\r
1141             {\r
1142                 StatusMessage = info.Title;\r
1143                 return;\r
1144             }\r
1145                         if (String.IsNullOrWhiteSpace(notification.Message) && String.IsNullOrWhiteSpace(notification.Title))\r
1146                                 return;\r
1147 \r
1148             if (notification.Level <= TraceLevel.Warning)\r
1149                             ShowBalloonFor(notification);\r
1150                 }\r
1151 \r
1152             private void ShowBalloonFor(Notification notification)\r
1153             {\r
1154             Contract.Requires(notification!=null);\r
1155             \r
1156             if (!Settings.ShowDesktopNotifications) \r
1157                 return;\r
1158             \r
1159             BalloonIcon icon;\r
1160                 switch (notification.Level)\r
1161                 {\r
1162                 case TraceLevel.Verbose:\r
1163                         return;\r
1164                     case TraceLevel.Info:                   \r
1165                         icon = BalloonIcon.Info;\r
1166                         break;\r
1167                 case TraceLevel.Error:\r
1168                     icon = BalloonIcon.Error;\r
1169                     break;\r
1170                 case TraceLevel.Warning:\r
1171                         icon = BalloonIcon.Warning;\r
1172                         break;\r
1173                     default:\r
1174                         return;\r
1175                 }\r
1176 \r
1177                 var tv = (ShellView) GetView();\r
1178                 System.Action clickAction = null;\r
1179                 if (notification is ExpirationNotification)\r
1180                 {\r
1181                     clickAction = () => ShowPreferences("AccountTab");\r
1182                 }\r
1183                 var balloon = new PithosBalloon\r
1184                                   {\r
1185                                       Title = notification.Title,\r
1186                                       Message = notification.Message,\r
1187                                       Icon = icon,\r
1188                                       ClickAction = clickAction\r
1189                                   };\r
1190                 tv.TaskbarView.ShowCustomBalloon(balloon, PopupAnimation.Fade, 4000);\r
1191             }\r
1192 \r
1193             #endregion\r
1194 \r
1195                 public void Handle(ShowFilePropertiesEvent message)\r
1196                 {\r
1197                         if (message == null)\r
1198                                 throw new ArgumentNullException("message");\r
1199                         if (String.IsNullOrWhiteSpace(message.FileName) )\r
1200                                 throw new ArgumentException("message");\r
1201                         Contract.EndContractBlock();\r
1202 \r
1203                         var fileName = message.FileName;\r
1204                         //TODO: Display file properties for non-container folders\r
1205                         if (File.Exists(fileName))\r
1206                                 //Retrieve the full name with exact casing. Pithos names are case sensitive                             \r
1207                                 ShowFileProperties(FileInfoExtensions.GetProperFilePathCapitalization(fileName));\r
1208                         else if (Directory.Exists(fileName))\r
1209                                 //Retrieve the full name with exact casing. Pithos names are case sensitive\r
1210                         {\r
1211                                 var path = FileInfoExtensions.GetProperDirectoryCapitalization(fileName);\r
1212                                 if (IsContainer(path))\r
1213                                         ShowContainerProperties(path);\r
1214                                 else\r
1215                                         ShowFileProperties(path);\r
1216                         }\r
1217                 }\r
1218 \r
1219                 private bool IsContainer(string path)\r
1220                 {\r
1221                         var matchingFolders = from account in _accounts\r
1222                                                                   from rootFolder in Directory.GetDirectories(account.AccountPath)\r
1223                                                                   where rootFolder.Equals(path, StringComparison.InvariantCultureIgnoreCase)\r
1224                                                                   select rootFolder;\r
1225                         return matchingFolders.Any();\r
1226                 }\r
1227 \r
1228                 public FileStatus GetFileStatus(string localFileName)\r
1229                 {\r
1230                         if (String.IsNullOrWhiteSpace(localFileName))\r
1231                                 throw new ArgumentNullException("localFileName");\r
1232                         Contract.EndContractBlock();\r
1233                         \r
1234                         var statusKeeper = IoC.Get<IStatusKeeper>();\r
1235                         var status=statusKeeper.GetFileStatus(localFileName);\r
1236                         return status;\r
1237                 }\r
1238 \r
1239             public void RemoveAccountFromDatabase(AccountSettings account)\r
1240             {\r
1241             var statusKeeper = IoC.Get<IStatusKeeper>();\r
1242             statusKeeper.ClearFolderStatus(account.RootPath);           \r
1243             }\r
1244         }\r
1245 }\r