Version
[pithos-ms-client] / trunk / Pithos.Client.WPF / Shell / ShellViewModel.cs
index 92cd900..f86f46e 100644 (file)
@@ -1,6 +1,45 @@
-using System.Collections.Concurrent;
-using System.ComponentModel;
-using System.ComponentModel.Composition;
+#region
+/* -----------------------------------------------------------------------
+ * <copyright file="ShellViewModel.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.Collections.Concurrent;
 using System.Diagnostics;
 using System.Diagnostics.Contracts;
 using System.IO;
@@ -8,30 +47,31 @@ using System.Net;
 using System.Reflection;
 using System.Runtime.InteropServices;
 using System.ServiceModel;
-using System.ServiceModel.Description;
 using System.Threading.Tasks;
 using System.Windows;
+using System.Windows.Controls.Primitives;
+using AppLimit.NetSparkle;
 using Caliburn.Micro;
 using Hardcodet.Wpf.TaskbarNotification;
 using Pithos.Client.WPF.Configuration;
 using Pithos.Client.WPF.FileProperties;
-using Pithos.Client.WPF.Properties;
+using Pithos.Client.WPF.Preferences;
 using Pithos.Client.WPF.SelectiveSynch;
 using Pithos.Client.WPF.Services;
 using Pithos.Client.WPF.Shell;
 using Pithos.Core;
+using Pithos.Core.Agents;
 using Pithos.Interfaces;
 using System;
 using System.Collections.Generic;
 using System.Linq;
-using System.Text;
 using Pithos.Network;
 using StatusService = Pithos.Client.WPF.Services.StatusService;
 
 namespace Pithos.Client.WPF {
-    using System.ComponentModel.Composition;
+       using System.ComponentModel.Composition;
 
-    
+       
        ///<summary>
        /// The "shell" of the Pithos application displays the taskbar  icon, menu and notifications.
        /// The shell also hosts the status service called by shell extensions to retrieve file info
@@ -44,19 +84,20 @@ namespace Pithos.Client.WPF {
        /// * ShowFilePropertiesEvent: Raised when a shell command requests the display of the file/container properties dialog
        ///</remarks>           
        //TODO: CODE SMELL Why does the shell handle the SelectiveSynchChanges?
-    [Export(typeof(IShell))]
-    public class ShellViewModel : Screen, IStatusNotification, IShell,
-        IHandle<Notification>, IHandle<SelectiveSynchChanges>, IHandle<ShowFilePropertiesEvent>
-    {
+    [Export(typeof(IShell)), Export(typeof(ShellViewModel))]
+       public class ShellViewModel : Screen, IStatusNotification, IShell,
+               IHandle<Notification>, IHandle<SelectiveSynchChanges>, IHandle<ShowFilePropertiesEvent>
+       {
+
                //The Status Checker provides the current synch state
                //TODO: Could we remove the status checker and use events in its place?
-        private IStatusChecker _statusChecker;
-        private IEventAggregator _events;
+               private readonly IStatusChecker _statusChecker;
+               private readonly IEventAggregator _events;
 
-        public PithosSettings Settings { get; private set; }        
+               public PithosSettings Settings { get; private set; }
 
-               
-        private Dictionary<string, PithosMonitor> _monitors = new Dictionary<string, PithosMonitor>();
+
+               private readonly ConcurrentDictionary<Uri, PithosMonitor> _monitors = new ConcurrentDictionary<Uri, PithosMonitor>();
                ///<summary>
                /// Dictionary of account monitors, keyed by account
                ///</summary>
@@ -66,677 +107,1084 @@ namespace Pithos.Client.WPF {
                ///</remarks>
                // TODO: Does the Shell REALLY need access to the monitors? Could we achieve the same results with a better design?
                // TODO: The monitors should be internal to Pithos.Core, even though exposing them makes coding of the Object and Container windows easier
-        public Dictionary<string, PithosMonitor> Monitors
-        {
-            get { return _monitors; }
-        }
+               public ConcurrentDictionary<Uri, PithosMonitor> Monitors
+               {
+                       get { return _monitors; }
+               }
 
 
                ///<summary>
                /// The status service is used by Shell extensions to retrieve file status information
                ///</summary>
                //TODO: CODE SMELL! This is the shell! While hosting in the shell makes executing start/stop commands easier, it is still a smell
-        private ServiceHost _statusService { get; set; }
+               private ServiceHost _statusService;
 
                //Logging in the Pithos client is provided by log4net
-        private static readonly log4net.ILog Log = log4net.LogManager.GetLogger("Pithos");
+        private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
 
-               ///<summary>
+           private readonly PollAgent _pollAgent;
+           private readonly NetworkAgent _networkAgent;
+
+           [Import]
+           public Selectives Selectives { get; set; }
+
+           private MiniStatusViewModel _miniStatus;
+
+           [Import]
+        public MiniStatusViewModel MiniStatus
+           {
+               get { return _miniStatus; }
+               set
+               {
+                   _miniStatus = value;
+                   _miniStatus.Shell = this;
+                   _miniStatus.Deactivated += (sender, arg) =>
+                                                  {
+                                                      _statusVisible = false;
+                                                   NotifyOfPropertyChange(()=>MiniStatusCaption);
+                                                  };
+               }
+           }
+
+           ///<summary>
                /// The Shell depends on MEF to provide implementations for windowManager, events, the status checker service and the settings
                ///</summary>
                ///<remarks>
                /// The PithosSettings class encapsulates the app's settings to abstract their storage mechanism (App settings, a database or registry)
                ///</remarks>
-        [ImportingConstructor]         
-        public ShellViewModel(IWindowManager windowManager, IEventAggregator events, IStatusChecker statusChecker, PithosSettings settings)
-        {
-            try
-            {
+               [ImportingConstructor]          
+               public ShellViewModel(IWindowManager windowManager, IEventAggregator events, IStatusChecker statusChecker, PithosSettings settings,PollAgent pollAgent,NetworkAgent networkAgent)
+               {
+                       try
+                       {
 
-                _windowManager = windowManager;
+                               _windowManager = windowManager;
                                //CHECK: Caliburn doesn't need explicit command construction
-                //OpenPithosFolderCommand = new PithosCommand(OpenPithosFolder);
-                _statusChecker = statusChecker;
+                               //OpenPithosFolderCommand = new PithosCommand(OpenPithosFolder);
+                               _statusChecker = statusChecker;
                                //The event subst
-                _events = events;
-                _events.Subscribe(this);
+                               _events = events;
+                               _events.Subscribe(this);
 
-                Settings = settings;
+                           _pollAgent = pollAgent;
+                           _networkAgent = networkAgent;
+                               Settings = settings;
 
-                StatusMessage = "In Synch";
+                               Proxy.SetFromSettings(settings);
 
-                _accounts.CollectionChanged += (sender, e) =>
-                                                   {
-                                                       NotifyOfPropertyChange(() => OpenFolderCaption);
-                                                       NotifyOfPropertyChange(() => HasAccounts);
-                                                   };
-
-            }
-            catch (Exception exc)
-            {
-                Log.Error("Error while starting the ShellViewModel",exc);
-                throw;
-            }
-        }
+                StatusMessage = Settings.Accounts.Count==0 
+                    ? "No Accounts added\r\nPlease add an account" 
+                    : "Starting";
 
+                               _accounts.CollectionChanged += (sender, e) =>
+                                                                                                  {
+                                                                                                          NotifyOfPropertyChange(() => OpenFolderCaption);
+                                                                                                          NotifyOfPropertyChange(() => HasAccounts);
+                                                                                                  };
 
-        protected override void OnActivate()
-        {
-            base.OnActivate();
+                SetVersionMessage();
+                       }
+                       catch (Exception exc)
+                       {
+                               Log.Error("Error while starting the ShellViewModel",exc);
+                               throw;
+                       }
 
-            StartMonitoring();                    
-        }
+               }
 
+           private void SetVersionMessage()
+           {
+               Assembly assembly = Assembly.GetExecutingAssembly();
+               var fileVersion = FileVersionInfo.GetVersionInfo(assembly.Location);
+               VersionMessage = String.Format("Pithos+ {0}", fileVersion.FileVersion);
+           }
 
-        private async Task StartMonitoring()
+        public void CurrentSyncStatus()
         {
-            try
-            {                  
-                foreach (var account in Settings.Accounts)
-                {
-                    await MonitorAccount(account);
-                }
-                _statusService = StatusService.Start();
+            if (Accounts.Count == 0)
+            {
+                ShowPreferences("AccountTab");
             }
-            catch (AggregateException exc)
+            else
             {
-                exc.Handle(e =>
+                if (!_statusVisible)
+                    _windowManager.ShowWindow(MiniStatus);
+                else
                 {
-                    Log.Error("Error while starting monitoring", e);
-                    return true;
-                });
-                throw;
+                    if (MiniStatus.IsActive)
+                        MiniStatus.TryClose();
+                }
+                _statusVisible = !_statusVisible;
+
+                NotifyOfPropertyChange(() => MiniStatusCaption);
             }
         }
 
-        protected override void OnDeactivate(bool close)
-        {
-            base.OnDeactivate(close);
-            if (close)
+           protected override void OnActivate()
+               {
+                       base.OnActivate();
+
+            InitializeSparkle();
+
+               //Must delay opening the upgrade window
+            //to avoid Windows Messages sent by the TaskbarIcon
+            TaskEx.Delay(5000).ContinueWith(_=>
+                Execute.OnUIThread(()=> _sparkle.StartLoop(true,Settings.UpdateForceCheck,Settings.UpdateCheckInterval)));
+
+
+                       StartMonitoring();                    
+               }
+
+
+           private void OnCheckFinished(object sender, bool updaterequired)
+           {
+            
+            Log.InfoFormat("Upgrade check finished. Need Upgrade: {0}", updaterequired);
+            if (_manualUpgradeCheck)
             {
-                StatusService.Stop(_statusService);
-                _statusService = null;
+                _manualUpgradeCheck = false;
+                if (!updaterequired)
+                    //Sparkle raises events on a background thread
+                    Execute.OnUIThread(()=>
+                        ShowBalloonFor(new Notification{Title="Pithos+ is up to date",Message="You have the latest Pithos+ version. No update is required"}));
             }
+           }
+
+           private void OnUpgradeDetected(object sender, UpdateDetectedEventArgs e)
+           {            
+               Log.InfoFormat("Update detected {0}",e.LatestVersion);
+           }
+
+        public void CheckForUpgrade()
+        {
+            ShowBalloonFor(new Notification{Title="Checking for upgrades",Message="Contacting the server to retrieve the latest Pithos+ version."});
+            _sparkle.StopLoop();
+            _sparkle.updateDetected -= OnUpgradeDetected;
+            _sparkle.checkLoopFinished -= OnCheckFinished;
+            _sparkle.Dispose();
+
+            _manualUpgradeCheck = true;
+            InitializeSparkle();
+            _sparkle.StartLoop(true,true,Settings.UpdateCheckInterval);
         }
 
-        public Task MonitorAccount(AccountSettings account)
+        private void InitializeSparkle()
         {
-            return Task.Factory.StartNew(() =>
-            {                                                
-                PithosMonitor monitor = null;
-                var accountName = account.AccountName;
+            _sparkle = new Sparkle(Settings.UpdateUrl);
+            _sparkle.updateDetected += OnUpgradeDetected;
+            _sparkle.checkLoopFinished += OnCheckFinished;
+            _sparkle.ShowDiagnosticWindow = Settings.UpdateDiagnostics;
+        }
 
-                if (_monitors.TryGetValue(accountName, out monitor))
+           private async void StartMonitoring()
+               {
+                       try
+                       {
+                if (Settings.IgnoreCertificateErrors)
                 {
-                    //If the account is active
+                    ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true;
+                }
+                    
+                               var accounts = Settings.Accounts.Select(MonitorAccount);
+                               await TaskEx.WhenAll(accounts);
+                               _statusService = StatusService.Start();
+
+                       }
+                       catch (AggregateException exc)
+                       {
+                               exc.Handle(e =>
+                               {
+                                       Log.Error("Error while starting monitoring", e);
+                                       return true;
+                               });
+                               throw;
+                       }
+               }
+
+               protected override void OnDeactivate(bool close)
+               {
+                       base.OnDeactivate(close);
+                       if (close)
+                       {
+                               StatusService.Stop(_statusService);
+                               _statusService = null;
+                       }
+               }
+
+               public Task MonitorAccount(AccountSettings account)
+               {
+                       return Task.Factory.StartNew(() =>
+                       {                                                
+                               PithosMonitor monitor;
+                               var accountName = account.AccountName;
+
+                           MigrateFolders(account);
+
+                           Selectives.SetIsSelectiveEnabled(account.AccountKey, account.SelectiveSyncEnabled);
+
+                               if (Monitors.TryGetValue(account.AccountKey, out monitor))
+                               {
+                                       //If the account is active
                     if (account.IsActive)
+                    {
+                        //The Api Key may have changed throuth the Preferences dialog
+                        monitor.ApiKey = account.ApiKey;
+                                               Debug.Assert(monitor.StatusNotification == this,"An existing monitor should already have a StatusNotification service object");
+                        monitor.RootPath = account.RootPath;
                         //Start the monitor. It's OK to start an already started monitor,
                         //it will just ignore the call                        
-                        StartMonitor(monitor).Wait();                        
+                        StartMonitor(monitor).Wait();
+                    }
                     else
                     {
                         //If the account is inactive
                         //Stop and remove the monitor
-                        RemoveMonitor(accountName);
+                        RemoveMonitor(account.ServerUrl,accountName);
                     }
-                    return;
-                }
-
-                //Create a new monitor/ Can't use MEF here, it would return a single instance for all monitors
-                monitor = new PithosMonitor
-                              {
-                                  UserName = accountName,
-                                  ApiKey = account.ApiKey,                                  
-                                  StatusNotification = this,
-                                  RootPath = account.RootPath
-                              };
-                //PithosMonitor uses MEF so we need to resolve it
-                IoC.BuildUp(monitor);
-
-                var appSettings = Properties.Settings.Default;
-                monitor.AuthenticationUrl = account.ServerUrl;
-
-                _monitors[accountName] = monitor;
+                                       return;
+                               }
+
+                               
+                               //Create a new monitor/ Can't use MEF here, it would return a single instance for all monitors
+                               monitor = new PithosMonitor
+                                                         {
+                                                                 UserName = accountName,
+                                                                 ApiKey = account.ApiKey,                                  
+                                                                 StatusNotification = this,
+                                                                 RootPath = account.RootPath
+                                                         };
+                               //PithosMonitor uses MEF so we need to resolve it
+                               IoC.BuildUp(monitor);
+
+                               monitor.AuthenticationUrl = account.ServerUrl;
+
+                               Monitors[account.AccountKey] = monitor;
+
+                               if (account.IsActive)
+                               {
+                                       //Don't start a monitor if it doesn't have an account and ApiKey
+                                       if (String.IsNullOrWhiteSpace(monitor.UserName) ||
+                                               String.IsNullOrWhiteSpace(monitor.ApiKey))
+                                               return;
+                                       StartMonitor(monitor);
+                               }
+                       });
+               }
+
+           private void MigrateFolders(AccountSettings account)
+           {
+               var oldOthersFolder=Path.Combine(account.RootPath, FolderConstants.OldOthersFolder);
+               var newOthersFolder = Path.Combine(account.RootPath, FolderConstants.OthersFolder);
+               var oldFolder = new DirectoryInfo(oldOthersFolder);
+               var newFolder = new DirectoryInfo(newOthersFolder);
 
-                if (account.IsActive)
-                {
-                    //Don't start a monitor if it doesn't have an account and ApiKey
-                    if (String.IsNullOrWhiteSpace(monitor.UserName) ||
-                        String.IsNullOrWhiteSpace(monitor.ApiKey))
-                        return;
-                    StartMonitor(monitor);
-                }
-            });
-        }
+            if (oldFolder.Exists && !newFolder.Exists)
+            {
+                oldFolder.MoveTo(newOthersFolder);
+            }
+           }
 
 
-        protected override void OnViewLoaded(object view)
-        {
-            UpdateStatus();
-            var window = (Window)view;            
-            TaskEx.Delay(1000).ContinueWith(t => Execute.OnUIThread(window.Hide));
-            base.OnViewLoaded(view);
-        }
+           protected override void OnViewLoaded(object view)
+               {
+                       UpdateStatus();
+                       var window = (Window)view;            
+                       TaskEx.Delay(1000).ContinueWith(t => Execute.OnUIThread(window.Hide));
+                       base.OnViewLoaded(view);
+               }
 
 
-        #region Status Properties
+               #region Status Properties
 
-        private string _statusMessage;
-        public string StatusMessage
-        {
-            get { return _statusMessage; }
-            set
-            {
-                _statusMessage = value;
-                NotifyOfPropertyChange(() => StatusMessage);
-            }
-        }
+               private string _statusMessage;
+               public string StatusMessage
+               {
+                       get { return _statusMessage; }
+                       set
+                       {
+                               _statusMessage = value;
+                               NotifyOfPropertyChange(() => StatusMessage);
+                NotifyOfPropertyChange(() => TooltipMessage);
+                       }
+               }
 
-        private readonly ObservableConcurrentCollection<AccountInfo> _accounts = new ObservableConcurrentCollection<AccountInfo>();
-        public ObservableConcurrentCollection<AccountInfo> Accounts
-        {
-            get { return _accounts; }
-        }
+        public string VersionMessage { get; set; }
 
-           public bool HasAccounts
+           public string TooltipMessage
            {
-            get { return _accounts.Count > 0; }
+               get
+               {
+                   return String.Format("{0}\r\n{1}",VersionMessage,StatusMessage);
+               }
            }
 
-
-        public string OpenFolderCaption
+        public string TooltipMiniStatus
         {
             get
             {
-                return (_accounts.Count == 0)
-                        ? "No Accounts Defined"
-                        : "Open Pithos Folder";
+                return String.Format("{0}\r\n{1}", "Status Window", "Enable / Disable the status window");
             }
         }
 
-        private string _pauseSyncCaption="Pause Synching";
-        public string PauseSyncCaption
+        public string ToggleStatusWindowMessage
         {
-            get { return _pauseSyncCaption; }
-            set
+            get
             {
-                _pauseSyncCaption = value;
-                NotifyOfPropertyChange(() => PauseSyncCaption);
+                return String.Format("{0}" + Environment.NewLine + "{1} Toggle Mini Status");
             }
         }
 
-        private readonly ObservableConcurrentCollection<FileEntry> _recentFiles = new ObservableConcurrentCollection<FileEntry>();
-        public ObservableConcurrentCollection<FileEntry> RecentFiles
-        {
-            get { return _recentFiles; }
-        }
-
-
-        private string _statusIcon="../Images/Pithos.ico";
-        public string StatusIcon
+           private readonly ObservableConcurrentCollection<AccountInfo> _accounts = new ObservableConcurrentCollection<AccountInfo>();
+               public ObservableConcurrentCollection<AccountInfo> Accounts
+               {
+                       get { return _accounts; }
+               }
+
+               public bool HasAccounts
+               {
+                       get { return _accounts.Count > 0; }
+               }
+
+
+               public string OpenFolderCaption
+               {
+                       get
+                       {
+                               return (_accounts.Count == 0)
+                                               ? "No Accounts Defined"
+                                               : "Open Pithos Folder";
+                       }
+               }
+
+               private string _pauseSyncCaption="Pause Synching";
+               public string PauseSyncCaption
+               {
+                       get { return _pauseSyncCaption; }
+                       set
+                       {
+                               _pauseSyncCaption = value;
+                               NotifyOfPropertyChange(() => PauseSyncCaption);
+                       }
+               }
+
+               private readonly ObservableConcurrentCollection<FileEntry> _recentFiles = new ObservableConcurrentCollection<FileEntry>();
+               public ObservableConcurrentCollection<FileEntry> RecentFiles
+               {
+                       get { return _recentFiles; }
+               }
+
+
+               private string _statusIcon="../Images/Pithos.ico";
+               public string StatusIcon
+               {
+                       get { return _statusIcon; }
+                       set
+                       {
+                               //TODO: Ensure all status icons use the Pithos logo
+                               _statusIcon = value;
+                               NotifyOfPropertyChange(() => StatusIcon);
+                       }
+               }
+
+               #endregion
+
+               #region Commands
+
+        public void CancelCurrentOperation()
         {
-            get { return _statusIcon; }
-            set
-            {
-                //_statusIcon = value;
-                NotifyOfPropertyChange(() => StatusIcon);
-            }
+            _networkAgent.CancelCurrentOperation();
         }
 
-        #endregion
-
-        #region Commands
-
         public void ShowPreferences()
         {
-            Settings.Reload();
-            var preferences = new PreferencesViewModel(_windowManager,_events, this,Settings);            
-            _windowManager.ShowDialog(preferences);
-            
+            ShowPreferences(null);
         }
 
-        public void AboutPithos()
-        {
-            var about = new AboutViewModel();
-            _windowManager.ShowWindow(about);
-        }
+               public void ShowPreferences(string currentTab)
+               {
+                       //Settings.Reload();
+            
+                   var preferences = new PreferencesViewModel(_windowManager, _events, this, Settings,currentTab);
+                   _windowManager.ShowDialog(preferences);
+                       
+               }
+
+               public void AboutPithos()
+               {
+                       var about = IoC.Get<AboutViewModel>();
+                   about.LatestVersion=_sparkle.LatestVersion;
+                       _windowManager.ShowWindow(about);
+               }
+
+               public void SendFeedback()
+               {
+                       var feedBack =  IoC.Get<FeedbackViewModel>();
+                       _windowManager.ShowWindow(feedBack);
+               }
+
+               //public PithosCommand OpenPithosFolderCommand { get; private set; }
+
+               public void OpenPithosFolder()
+               {
+                       var account = Settings.Accounts.FirstOrDefault(acc => acc.IsActive);
+                       if (account == null)
+                               return;
+                       Process.Start(account.RootPath);
+               }
+
+               public void OpenPithosFolder(AccountInfo account)
+               {
+                       Process.Start(account.AccountPath);
+               }
 
-        public void SendFeedback()
-        {
-            var feedBack =  IoC.Get<FeedbackViewModel>();
-            _windowManager.ShowWindow(feedBack);
-        }
+               
 
-        //public PithosCommand OpenPithosFolderCommand { get; private set; }
+               public void GoToSite()
+               {            
+                       var site = Properties.Settings.Default.ProductionServer;
+                       Process.Start(site);            
+               }
 
-        public void OpenPithosFolder()
-        {
-            var account = Settings.Accounts.FirstOrDefault(acc => acc.IsActive);
-            if (account == null)
-                return;
-            Process.Start(account.RootPath);
-        }
 
-        public void OpenPithosFolder(AccountInfo account)
-        {
-            Process.Start(account.AccountPath);
-        }
+               public void GoToSite(AccountInfo account)
+               {
+                   var uri = account.SiteUri.Replace("http://","https://");            
+                   Process.Start(uri);
+               }
 
-        
-/*
-        public void GoToSite()
-        {            
-            var site = Properties.Settings.Default.PithosSite;
-            Process.Start(site);            
-        }
-*/
+           private bool _statusVisible;
 
-        public void GoToSite(AccountInfo account)
-        {
-            /*var site = String.Format("{0}/ui/?token={1}&user={2}",
-                account.SiteUri,account.Token,
-                account.UserName);*/
-            Process.Start(account.SiteUri);
-        }
+           public string MiniStatusCaption
+           {
+               get
+               {
+                   return  _statusVisible ? "Hide Status Window" : "Show Status Window";
+               }
+           }
 
-        public void ShowFileProperties()
+           public bool HasConflicts
+           {
+            get { return true; }
+           }
+        public void ShowConflicts()
         {
-            var account = Settings.Accounts.First(acc => acc.IsActive);            
-            var dir = new DirectoryInfo(account.RootPath + @"\pithos");
-            var files=dir.GetFiles();
-            var r=new Random();
-            var idx=r.Next(0, files.Length);
-            ShowFileProperties(files[idx].FullName);            
+            _windowManager.ShowWindow(IoC.Get<ConflictsViewModel>());            
         }
 
-        public void ShowFileProperties(string filePath)
+           /// <summary>
+        /// Open an explorer window to the target path's directory
+        /// and select the file
+        /// </summary>
+        /// <param name="entry"></param>
+        public void GoToFile(FileEntry entry)
         {
-            if (String.IsNullOrWhiteSpace(filePath))
-                throw new ArgumentNullException("filePath");
-            if (!File.Exists(filePath))
-                throw new ArgumentException(String.Format("Non existent file {0}",filePath),"filePath");
-            Contract.EndContractBlock();
-
-            var pair=(from monitor in  Monitors
-                               where filePath.StartsWith(monitor.Value.RootPath, StringComparison.InvariantCultureIgnoreCase)
-                                   select monitor).FirstOrDefault();
-            var account = pair.Key;
-            var accountMonitor = pair.Value;
-
-            if (accountMonitor == null)
+            var fullPath = entry.FullPath;
+            if (!File.Exists(fullPath) && !Directory.Exists(fullPath))
                 return;
-
-            var infoTask=Task.Factory.StartNew(()=>accountMonitor.GetObjectInfo(filePath));
-
-            
-
-            var fileProperties = new FilePropertiesViewModel(this, infoTask,filePath);
-            _windowManager.ShowWindow(fileProperties);
-        } 
-        
-        public void ShowContainerProperties()
-        {
-            var account = Settings.Accounts.First(acc => acc.IsActive);            
-            var dir = new DirectoryInfo(account.RootPath);
-            var fullName = (from folder in dir.EnumerateDirectories()
-                            where (folder.Attributes & FileAttributes.Hidden) == 0
-                            select folder.FullName).First();
-            ShowContainerProperties(fullName);            
-        }
-
-        public void ShowContainerProperties(string filePath)
-        {
-            if (String.IsNullOrWhiteSpace(filePath))
-                throw new ArgumentNullException("filePath");
-            if (!Directory.Exists(filePath))
-                throw new ArgumentException(String.Format("Non existent file {0}",filePath),"filePath");
-            Contract.EndContractBlock();
-
-            var pair=(from monitor in  Monitors
-                               where filePath.StartsWith(monitor.Value.RootPath, StringComparison.InvariantCultureIgnoreCase)
-                                   select monitor).FirstOrDefault();
-            var account = pair.Key;
-            var accountMonitor = pair.Value;            
-            var info = accountMonitor.GetContainerInfo(filePath);
-
-            
-
-            var containerProperties = new ContainerPropertiesViewModel(this, info,filePath);
-            _windowManager.ShowWindow(containerProperties);
+            Process.Start("explorer.exe","/select, " + fullPath);
         }
 
-        public ObjectInfo RefreshObjectInfo(ObjectInfo currentInfo)
+        public void OpenLogPath()
         {
-            if (currentInfo==null)
-                throw new ArgumentNullException("currentInfo");
-            Contract.EndContractBlock();
+            var pithosDataPath = PithosSettings.PithosDataPath;
 
-            var monitor = Monitors[currentInfo.Account];
-            var newInfo=monitor.CloudClient.GetObjectInfo(currentInfo.Account, currentInfo.Container, currentInfo.Name);
-            return newInfo;
-        }
-
-        public ContainerInfo RefreshContainerInfo(ContainerInfo container)
-        {
-            if (container == null)
-                throw new ArgumentNullException("container");
-            Contract.EndContractBlock();
-
-            var monitor = Monitors[container.Account];
-            var newInfo = monitor.CloudClient.GetContainerInfo(container.Account, container.Name);
-            return newInfo;
+            Process.Start(pithosDataPath);
         }
+        
+        public void ShowFileProperties()
+               {
+                       var account = Settings.Accounts.First(acc => acc.IsActive);            
+                       var dir = new DirectoryInfo(account.RootPath + @"\pithos");
+                       var files=dir.GetFiles();
+                       var r=new Random();
+                       var idx=r.Next(0, files.Length);
+                       ShowFileProperties(files[idx].FullName);            
+               }
+
+               public void ShowFileProperties(string filePath)
+               {
+                       if (String.IsNullOrWhiteSpace(filePath))
+                               throw new ArgumentNullException("filePath");
+                       if (!File.Exists(filePath) && !Directory.Exists(filePath))
+                               throw new ArgumentException(String.Format("Non existent file {0}",filePath),"filePath");
+                       Contract.EndContractBlock();
+
+                       var pair=(from monitor in  Monitors
+                                                          where filePath.StartsWith(monitor.Value.RootPath, StringComparison.InvariantCultureIgnoreCase)
+                                                                  select monitor).FirstOrDefault();
+                       var accountMonitor = pair.Value;
+
+                       if (accountMonitor == null)
+                               return;
+
+                       var infoTask=Task.Factory.StartNew(()=>accountMonitor.GetObjectInfo(filePath));
+
+                       
+
+                       var fileProperties = new FilePropertiesViewModel(this, infoTask,filePath);
+                       _windowManager.ShowWindow(fileProperties);
+               } 
+               
+               public void ShowContainerProperties()
+               {
+                       var account = Settings.Accounts.First(acc => acc.IsActive);            
+                       var dir = new DirectoryInfo(account.RootPath);
+                       var fullName = (from folder in dir.EnumerateDirectories()
+                                                       where (folder.Attributes & FileAttributes.Hidden) == 0
+                                                       select folder.FullName).First();
+                       ShowContainerProperties(fullName);            
+               }
+
+               public void ShowContainerProperties(string filePath)
+               {
+                       if (String.IsNullOrWhiteSpace(filePath))
+                               throw new ArgumentNullException("filePath");
+                       if (!Directory.Exists(filePath))
+                               throw new ArgumentException(String.Format("Non existent file {0}",filePath),"filePath");
+                       Contract.EndContractBlock();
+
+                       var pair=(from monitor in  Monitors
+                                                          where filePath.StartsWith(monitor.Value.RootPath, StringComparison.InvariantCultureIgnoreCase)
+                                                                  select monitor).FirstOrDefault();
+                       var accountMonitor = pair.Value;            
+                       var info = accountMonitor.GetContainerInfo(filePath);
+
+                       
+
+                       var containerProperties = new ContainerPropertiesViewModel(this, info,filePath);
+                       _windowManager.ShowWindow(containerProperties);
+               }
+
+               public void SynchNow()
+               {
+                       _pollAgent.SynchNow();
+               }
+
+               public ObjectInfo RefreshObjectInfo(ObjectInfo currentInfo)
+               {
+                       if (currentInfo==null)
+                               throw new ArgumentNullException("currentInfo");
+                       Contract.EndContractBlock();                
+            var monitor = Monitors[currentInfo.AccountKey];
+                       var newInfo=monitor.CloudClient.GetObjectInfo(currentInfo.Account, currentInfo.Container, currentInfo.Name);
+                       return newInfo;
+               }
+
+               public ContainerInfo RefreshContainerInfo(ContainerInfo container)
+               {
+                       if (container == null)
+                               throw new ArgumentNullException("container");
+                       Contract.EndContractBlock();
+
+                       var monitor = Monitors[container.AccountKey];
+                       var newInfo = monitor.CloudClient.GetContainerInfo(container.Account, container.Name);
+                       return newInfo;
+               }
+
+           private bool _isPaused;
+           public bool IsPaused
+           {
+               get { return _isPaused; }
+               set
+               {
+                   _isPaused = value;
+                PauseSyncCaption = IsPaused ? "Resume syncing" : "Pause syncing";
+                var iconKey = IsPaused ? "TraySyncPaused" : "TrayInSynch";
+                StatusIcon = String.Format(@"../Images/{0}.ico", iconKey);
+
+                NotifyOfPropertyChange(() => IsPaused);
+               }
+           }
 
+           public void ToggleSynching()
+               {
+                       IsPaused=!IsPaused;
+                       foreach (var monitor in Monitors.Values)
+                       {
+                           monitor.Pause = IsPaused ;
+                       }
+            _pollAgent.Pause = IsPaused;
+            _networkAgent.Pause = IsPaused;
 
-        public void ToggleSynching()
-        {
-            bool isPaused=false;
-            foreach (var pair in Monitors)
-            {
-                var monitor = pair.Value;
-                monitor.Pause = !monitor.Pause;
-                isPaused = monitor.Pause;
-            }
 
-            PauseSyncCaption = isPaused ? "Resume syncing" : "Pause syncing";
-            var iconKey = isPaused? "TraySyncPaused" : "TrayInSynch";
-            StatusIcon = String.Format(@"../Images/{0}.ico", iconKey);
-        }
+               }
 
         public void ExitPithos()
         {
-            foreach (var pair in Monitors)
+            try
             {
-                var monitor = pair.Value;
-                monitor.Stop();
-            }
-
-            ((Window)GetView()).Close();
-        }
-        #endregion
 
+                foreach (var monitor in Monitors.Select(pair => pair.Value))
+                {
+                    monitor.Stop();
+                }
 
-        private Dictionary<PithosStatus, StatusInfo> iconNames = new List<StatusInfo>
+                var view = GetView() as Window;
+                if (view != null)
+                    view.Close();
+            }
+            catch (Exception exc)
             {
-                new StatusInfo(PithosStatus.InSynch, "All files up to date", "TrayInSynch"),
-                new StatusInfo(PithosStatus.Syncing, "Syncing Files", "TraySynching"),
-                new StatusInfo(PithosStatus.SyncPaused, "Sync Paused", "TraySyncPaused")
-            }.ToDictionary(s => s.Status);
-
-        readonly IWindowManager _windowManager;
-
-
-               ///<summary>
-               /// Updates the visual status indicators of the application depending on status changes, e.g. icon, stat                
-               ///</summary>
-        public void UpdateStatus()
-        {
-            var pithosStatus = _statusChecker.GetPithosStatus();
-
-            if (iconNames.ContainsKey(pithosStatus))
+                Log.Info("Exception while exiting", exc);                
+            }
+            finally
             {
-                var info = iconNames[pithosStatus];
-                StatusIcon = String.Format(@"../Images/{0}.ico", info.IconName);
-
-                Assembly assembly = Assembly.GetExecutingAssembly();                               
-                var fileVersion = FileVersionInfo.GetVersionInfo(assembly.Location);
-
-
-                StatusMessage = String.Format("Pithos {0}\r\n{1}", fileVersion.FileVersion,info.StatusText);
+                Application.Current.Shutdown();
             }
-            
-            _events.Publish(new Notification { Title = "Start", Message = "Start Monitoring", Level = TraceLevel.Info});
         }
 
+           #endregion
 
-       
-        private Task StartMonitor(PithosMonitor monitor,int retries=0)
-        {
-            return Task.Factory.StartNew(() =>
-            {
-                using (log4net.ThreadContext.Stacks["Monitor"].Push("Start"))
-                {
-                    try
-                    {
-                        Log.InfoFormat("Start Monitoring {0}", monitor.UserName);
 
-                        monitor.Start();
-                    }
-                    catch (WebException exc)
-                    {
-                        if (AbandonRetry(monitor, retries))
-                            return;
-
-                        if (IsUnauthorized(exc))
-                        {
-                            var message = String.Format("API Key Expired for {0}. Starting Renewal",monitor.UserName);                            
-                            Log.Error(message,exc);
-                            TryAuthorize(monitor,retries).Wait();
-                        }
-                        else
-                        {
-                            TryLater(monitor, exc,retries);
-                        }
-                    }
-                    catch (Exception exc)
-                    {
-                        if (AbandonRetry(monitor, retries)) 
-                            return;
+               private readonly Dictionary<PithosStatus, StatusInfo> _iconNames = new List<StatusInfo>
+                       {
+                               new StatusInfo(PithosStatus.InSynch, "All files up to date", "TrayInSynch"),
+                               new StatusInfo(PithosStatus.PollSyncing, "Polling Files", "TraySynching"),
+                new StatusInfo(PithosStatus.LocalSyncing, "Syncing Files", "TraySynching"),
+                               new StatusInfo(PithosStatus.SyncPaused, "Sync Paused", "TraySyncPaused")
+                       }.ToDictionary(s => s.Status);
 
-                        TryLater(monitor,exc,retries);
-                    }
-                }
-            });
-        }
+               readonly IWindowManager _windowManager;
+               
+        //private int _syncCount=0;
 
-        private bool AbandonRetry(PithosMonitor monitor, int retries)
-        {
-            if (retries > 1)
-            {
-                var message = String.Format("Monitoring of account {0} has failed too many times. Will not retry",
-                                            monitor.UserName);
-                _events.Publish(new Notification
-                                    {Title = "Account monitoring failed", Message = message, Level = TraceLevel.Error});
-                return true;
-            }
-            return false;
-        }
 
+        private PithosStatus _pithosStatus = PithosStatus.Disconnected;
 
-        private async Task TryAuthorize(PithosMonitor monitor,int retries)
+        public void SetPithosStatus(PithosStatus status)
         {
-            _events.Publish(new Notification { Title = "Authorization failed", Message = "Your API Key has probably expired. You will be directed to a page where you can renew it", Level = TraceLevel.Error });
-
-            try
-            {
-
-                var credentials = await PithosAccount.RetrieveCredentials(Settings.PithosLoginUrl);
-
-                var account = Settings.Accounts.FirstOrDefault(act => act.AccountName == credentials.UserName);
-                account.ApiKey = credentials.Password;
-                monitor.ApiKey = credentials.Password;
-                Settings.Save();
-                await TaskEx.Delay(10000);
-                StartMonitor(monitor, retries + 1);
-                NotifyOfPropertyChange(()=>Accounts);
-            }
-            catch (AggregateException exc)
-            {
-                string message = String.Format("API Key retrieval for {0} failed", monitor.UserName);
-                Log.Error(message, exc.InnerException);
-                _events.Publish(new Notification { Title = "Authorization failed", Message = message, Level = TraceLevel.Error });
+            if (_pithosStatus == PithosStatus.LocalSyncing && status == PithosStatus.PollComplete)
                 return;
-            }
-            catch (Exception exc)
-            {
-                string message = String.Format("API Key retrieval for {0} failed", monitor.UserName);
-                Log.Error(message, exc);
-                _events.Publish(new Notification { Title = "Authorization failed", Message = message, Level = TraceLevel.Error });
+            if (_pithosStatus == PithosStatus.PollSyncing && status == PithosStatus.LocalComplete)
                 return;
-                
-            }
-
-        }
-
-        private static bool IsUnauthorized(WebException exc)
-        {
-            if (exc==null)
-                throw new ArgumentNullException("exc");
-            Contract.EndContractBlock();
-
-            var response = exc.Response as HttpWebResponse;
-            if (response == null)
-                return false;
-            return (response.StatusCode == HttpStatusCode.Unauthorized);
-        }
-
-        private void TryLater(PithosMonitor monitor, Exception exc,int retries)
-        {
-            var message = String.Format("An exception occured. Can't start monitoring\nWill retry in 10 seconds");
-            Task.Factory.StartNewDelayed(10000, () => StartMonitor(monitor,retries+1));
-            _events.Publish(new Notification
-                                {Title = "Error", Message = message, Level = TraceLevel.Error});
-            Log.Error(message, exc);
-        }
-
-
-        public void NotifyChange(string status, TraceLevel level=TraceLevel.Info)
-        {
-            this.StatusMessage = status;
-            
-            _events.Publish(new Notification { Title = "Pithos", Message = status, Level = level });
+            if (status == PithosStatus.LocalComplete || status == PithosStatus.PollComplete)
+                _pithosStatus = PithosStatus.InSynch;
+            else
+                _pithosStatus = status;
+            UpdateStatus();
         }
 
-        public void NotifyChangedFile(string filePath)
+        public void SetPithosStatus(PithosStatus status,string message)
         {
-            var entry = new FileEntry {FullPath=filePath};
-            IProducerConsumerCollection<FileEntry> files=this.RecentFiles;
-            FileEntry popped;
-            while (files.Count > 5)
-                files.TryTake(out popped);
-            files.TryAdd(entry);
+            StatusMessage = message;
+            SetPithosStatus(status);
         }
 
-        public void NotifyAccount(AccountInfo account)
-        {
-            if (account== null)
-                return;
-            //TODO: What happens to an existing account whose Token has changed?
-            account.SiteUri= String.Format("{0}/ui/?token={1}&user={2}",
-                account.SiteUri, account.Token,
-                account.UserName);
-
-            if (Accounts.All(item => item.UserName != account.UserName))
-                Accounts.TryAdd(account);
+         /*  public Notifier GetNotifier(Notification startNotification, Notification endNotification)
+           {
+               return new Notifier(this, startNotification, endNotification);
+           }*/
 
-        }
+           public Notifier GetNotifier(string startMessage, string endMessage, params object[] args)
+           {
+               return new Notifier(this, 
+                new StatusNotification(String.Format(startMessage,args)), 
+                new StatusNotification(String.Format(endMessage,args)));
+           }
 
 
-        public void RemoveMonitor(string accountName)
-        {
-            if (String.IsNullOrWhiteSpace(accountName))
+           ///<summary>
+               /// Updates the visual status indicators of the application depending on status changes, e.g. icon, stat                
+               ///</summary>
+               public void UpdateStatus()
+               {
+
+                       if (_iconNames.ContainsKey(_pithosStatus))
+                       {
+                               var info = _iconNames[_pithosStatus];
+                               StatusIcon = String.Format(@"../Images/{0}.ico", info.IconName);
+                       }
+
+            if (_pithosStatus == PithosStatus.InSynch)
+                StatusMessage = "All files up to date";
+               }
+
+
+          
+               private Task StartMonitor(PithosMonitor monitor,int retries=0)
+               {
+                       return Task.Factory.StartNew(() =>
+                       {
+                               using (log4net.ThreadContext.Stacks["Monitor"].Push("Start"))
+                               {
+                                       try
+                                       {
+                                               Log.InfoFormat("Start Monitoring {0}", monitor.UserName);
+
+                                               monitor.Start();
+                                       }
+                                       catch (WebException exc)
+                                       {
+                                               if (AbandonRetry(monitor, retries))
+                                                       return;
+
+                                               HttpStatusCode statusCode =HttpStatusCode.OK;
+                                               var response = exc.Response as HttpWebResponse;
+                                               if(response!=null)
+                                                       statusCode = response.StatusCode;
+
+                                               switch (statusCode)
+                                               {
+                                                       case HttpStatusCode.Unauthorized:
+                                                               var message = String.Format("API Key Expired for {0}. Starting Renewal",
+                                                                                                                       monitor.UserName);
+                                                               Log.Error(message, exc);
+                                var account = Settings.Accounts.Find(acc => acc.AccountKey == new Uri(new Uri(monitor.AuthenticationUrl), monitor.UserName));                                
+                                                       account.IsExpired = true;
+                                Notify(new ExpirationNotification(account));
+                                                               //TryAuthorize(monitor.UserName, retries).Wait();
+                                                               break;
+                                                       case HttpStatusCode.ProxyAuthenticationRequired:
+                                                               TryAuthenticateProxy(monitor,retries);
+                                                               break;
+                                                       default:
+                                                               TryLater(monitor, exc, retries);
+                                                               break;
+                                               }
+                                       }
+                                       catch (Exception exc)
+                                       {
+                                               if (AbandonRetry(monitor, retries)) 
+                                                       return;
+
+                                               TryLater(monitor,exc,retries);
+                                       }
+                               }
+                       });
+               }
+
+               private void TryAuthenticateProxy(PithosMonitor monitor,int retries)
+               {
+                       Execute.OnUIThread(() =>
+                                                                  {                                       
+                                                                          var proxyAccount = IoC.Get<ProxyAccountViewModel>();
+                                                                               proxyAccount.Settings = Settings;
+                                                                          if (true != _windowManager.ShowDialog(proxyAccount)) 
+                                                                                  return;
+                                                                          StartMonitor(monitor, retries);
+                                                                          NotifyOfPropertyChange(() => Accounts);
+                                                                  });
+               }
+
+               private bool AbandonRetry(PithosMonitor monitor, int retries)
+               {
+                       if (retries > 1)
+                       {
+                               var message = String.Format("Monitoring of account {0} has failed too many times. Will not retry",
+                                                                                       monitor.UserName);
+                               _events.Publish(new Notification
+                                                                       {Title = "Account monitoring failed", Message = message, Level = TraceLevel.Error});
+                               return true;
+                       }
+                       return false;
+               }
+
+
+           private void TryLater(PithosMonitor monitor, Exception exc,int retries)
+               {
+                       var message = String.Format("An exception occured. Can't start monitoring\nWill retry in 10 seconds");
+                       Task.Factory.StartNewDelayed(10000, () => StartMonitor(monitor,retries+1));
+                       _events.Publish(new Notification
+                                                               {Title = "Error", Message = message, Level = TraceLevel.Error});
+                       Log.Error(message, exc);
+               }
+
+
+               public void NotifyChange(string status, TraceLevel level=TraceLevel.Info)
+               {
+                       StatusMessage = status;
+                       
+                       _events.Publish(new Notification { Title = "Pithos+", Message = status, Level = level });
+               }
+
+               public void NotifyChangedFile(string filePath)
+               {
+            if (RecentFiles.Any(e => e.FullPath == filePath))
                 return;
-
-            var accountInfo=_accounts.FirstOrDefault(account => account.UserName == accountName);
-            _accounts.TryRemove(accountInfo);
-
-            PithosMonitor monitor;
-            if (Monitors.TryGetValue(accountName, out monitor))
+            
+                       IProducerConsumerCollection<FileEntry> files=RecentFiles;
+                       FileEntry popped;
+                       while (files.Count > 5)
+                               files.TryTake(out popped);
+            var entry = new FileEntry { FullPath = filePath };
+                       files.TryAdd(entry);
+               }
+
+               public void NotifyAccount(AccountInfo account)
+               {
+                       if (account== null)
+                               return;
+                       //TODO: What happens to an existing account whose Token has changed?
+                       account.SiteUri= String.Format("{0}/ui/?token={1}&user={2}",
+                               account.SiteUri, Uri.EscapeDataString(account.Token),
+                               Uri.EscapeDataString(account.UserName));
+
+                       if (!Accounts.Any(item => item.UserName == account.UserName && item.SiteUri == account.SiteUri))
+                               Accounts.TryAdd(account);
+
+               }
+
+               public void NotifyConflicts(IEnumerable<FileSystemInfo> conflictFiles, string message)
+               {
+                       if (conflictFiles == null)
+                               return;
+                   //Convert to list to avoid multiple iterations
+            var files = conflictFiles.ToList();
+                       if (files.Count==0)
+                               return;
+
+                       UpdateStatus();
+                       //TODO: Create a more specific message. For now, just show a warning
+                       NotifyForFiles(files,message,TraceLevel.Warning);
+
+               }
+
+               public void NotifyForFiles(IEnumerable<FileSystemInfo> files, string message,TraceLevel level=TraceLevel.Info)
+               {
+                       if (files == null)
+                               return;
+                       if (!files.Any())
+                               return;
+
+                       StatusMessage = message;
+
+                       _events.Publish(new Notification { Title = "Pithos+", Message = message, Level = level});
+               }
+
+               public void Notify(Notification notification)
+               {
+                       _events.Publish(notification);
+               }
+
+
+               public void RemoveMonitor(string serverUrl,string accountName)
+               {
+                       if (String.IsNullOrWhiteSpace(accountName))
+                               return;
+
+                       var accountInfo=_accounts.FirstOrDefault(account => account.UserName == accountName && account.StorageUri.ToString().StartsWith(serverUrl));
+            if (accountInfo != null)
             {
-                Monitors.Remove(accountName);
-                monitor.Stop();
+                _accounts.TryRemove(accountInfo);
+                _pollAgent.RemoveAccount(accountInfo);
             }
-        }
 
-        public void RefreshOverlays()
-        {
-            foreach (var pair in Monitors)
+            var accountKey = new Uri(new Uri(serverUrl),accountName);
+                   PithosMonitor monitor;
+                       if (Monitors.TryRemove(accountKey, out monitor))
+                       {
+                               monitor.Stop();
+                //TODO: Also remove any pending actions for this account
+                //from the network queue                
+                       }
+               }
+
+               public void RefreshOverlays()
+               {
+                       foreach (var pair in Monitors)
+                       {
+                               var monitor = pair.Value;
+
+                               var path = monitor.RootPath;
+
+                               if (String.IsNullOrWhiteSpace(path))
+                                       continue;
+
+                               if (!Directory.Exists(path) && !File.Exists(path))
+                                       continue;
+
+                               IntPtr pathPointer = Marshal.StringToCoTaskMemAuto(path);
+
+                               try
+                               {
+                                       NativeMethods.SHChangeNotify(HChangeNotifyEventID.SHCNE_UPDATEITEM,
+                                                                                                HChangeNotifyFlags.SHCNF_PATHW | HChangeNotifyFlags.SHCNF_FLUSHNOWAIT,
+                                                                                                pathPointer, IntPtr.Zero);
+                               }
+                               finally
+                               {
+                                       Marshal.FreeHGlobal(pathPointer);
+                               }
+                       }
+               }
+
+               #region Event Handlers
+               
+               public void Handle(SelectiveSynchChanges message)
+               {
+                   TaskEx.Run(() =>
+                   {
+                       PithosMonitor monitor;
+                       if (Monitors.TryGetValue(message.Account.AccountKey, out monitor))
+                       {
+                    Selectives.SetIsSelectiveEnabled(message.Account.AccountKey, message.Enabled);
+                           monitor.SetSelectivePaths(message.Uris, message.Added, message.Removed);
+                       }
+
+                       var account = Accounts.FirstOrDefault(acc => acc.AccountKey == message.Account.AccountKey);
+                       if (account != null)
+                       {
+                           this._pollAgent.SetSelectivePaths(account, message.Added, message.Removed);
+                       }
+                   });
+
+               }
+
+
+               private bool _pollStarted;
+           private Sparkle _sparkle;
+           private bool _manualUpgradeCheck;
+
+           //SMELL: Doing so much work for notifications in the shell is wrong
+               //The notifications should be moved to their own view/viewmodel pair
+               //and different templates should be used for different message types
+               //This will also allow the addition of extra functionality, eg. actions
+               //
+               public void Handle(Notification notification)
+               {
+                       UpdateStatus();
+
+                       if (!Settings.ShowDesktopNotifications)
+                               return;
+
+                       if (notification is PollNotification)
+                       {
+                               _pollStarted = true;
+                               return;
+                       }
+                       if (notification is CloudNotification)
+                       {
+                               if (!_pollStarted) 
+                                       return;
+                               _pollStarted= false;
+                               notification.Title = "Pithos+";
+                               notification.Message = "Start Synchronisation";
+                       }
+
+                   var deleteNotification = notification as CloudDeleteNotification;
+            if (deleteNotification != null)
             {
-                var monitor = pair.Value;
-
-                var path = monitor.RootPath;
-
-                if (String.IsNullOrWhiteSpace(path))
-                    continue;
-
-                if (!Directory.Exists(path) && !File.Exists(path))
-                    continue;
-
-                IntPtr pathPointer = Marshal.StringToCoTaskMemAuto(path);
-
-                try
-                {
-                    NativeMethods.SHChangeNotify(HChangeNotifyEventID.SHCNE_UPDATEITEM,
-                                                 HChangeNotifyFlags.SHCNF_PATHW | HChangeNotifyFlags.SHCNF_FLUSHNOWAIT,
-                                                 pathPointer, IntPtr.Zero);
-                }
-                finally
-                {
-                    Marshal.FreeHGlobal(pathPointer);
-                }
+                StatusMessage = String.Format("Deleted {0}", deleteNotification.Data.Name);
+                return;
             }
-        }
 
-        #region Event Handlers
-        
-        public void Handle(SelectiveSynchChanges message)
-        {
-            var accountName = message.Account.AccountName;
-            PithosMonitor monitor;
-            if (_monitors.TryGetValue(accountName, out monitor))
+                   var progress = notification as ProgressNotification;
+                   
+                   
+            if (progress != null)
+                   {
+                       StatusMessage = String.Format("{0} {1:p2} of {2} - {3}",                                                      
+                                              progress.Action,
+                                                     progress.Block/(double)progress.TotalBlocks,
+                                                     progress.FileSize.ToByteSize(),
+                                                     progress.FileName);
+                       return;
+                   }
+
+                   var info = notification as StatusNotification;
+            if (info != null)
             {
-                monitor.AddSelectivePaths(message.Added);
-                monitor.RemoveSelectivePaths(message.Removed);
-
+                StatusMessage = info.Title;
+                return;
             }
-            
-        }
+                       if (String.IsNullOrWhiteSpace(notification.Message) && String.IsNullOrWhiteSpace(notification.Title))
+                               return;
 
+            if (notification.Level <= TraceLevel.Warning)
+                           ShowBalloonFor(notification);
+               }
 
-        public void Handle(Notification notification)
-        {
-            if (!Settings.ShowDesktopNotifications)
+           private void ShowBalloonFor(Notification notification)
+           {
+            Contract.Requires(notification!=null);
+            
+            if (!Settings.ShowDesktopNotifications) 
                 return;
-            BalloonIcon icon = BalloonIcon.None;
-            switch (notification.Level)
-            {
+            
+            BalloonIcon icon;
+               switch (notification.Level)
+               {
+                case TraceLevel.Verbose:
+                       return;
+                   case TraceLevel.Info:                   
+                       icon = BalloonIcon.Info;
+                       break;
                 case TraceLevel.Error:
                     icon = BalloonIcon.Error;
                     break;
-                case TraceLevel.Info:
-                case TraceLevel.Verbose:
-                    icon = BalloonIcon.Info;
-                    break;
                 case TraceLevel.Warning:
-                    icon = BalloonIcon.Warning;
-                    break;
-                default:
-                    icon = BalloonIcon.None;
-                    break;
-            }
-
-            if (Settings.ShowDesktopNotifications)
-            {
-                var tv = (ShellView) this.GetView();
-                tv.TaskbarView.ShowBalloonTip(notification.Title, notification.Message, icon);
-            }
-        }
-        #endregion
+                       icon = BalloonIcon.Warning;
+                       break;
+                   default:
+                       return;
+               }
+
+               var tv = (ShellView) GetView();
+               System.Action clickAction = null;
+               if (notification is ExpirationNotification)
+               {
+                   clickAction = () => ShowPreferences("AccountTab");
+               }
+               var balloon = new PithosBalloon
+                                 {
+                                     Title = notification.Title,
+                                     Message = notification.Message,
+                                     Icon = icon,
+                                     ClickAction = clickAction
+                                 };
+               tv.TaskbarView.ShowCustomBalloon(balloon, PopupAnimation.Fade, 4000);
+           }
 
-        public void Handle(ShowFilePropertiesEvent message)
-        {
-            if (message == null)
-                throw new ArgumentNullException("message");
-            if (String.IsNullOrWhiteSpace(message.FileName) )
-                throw new ArgumentException("message");
-            Contract.EndContractBlock();
-
-            var fileName = message.FileName;
-
-            if (File.Exists(fileName))
-                this.ShowFileProperties(fileName);
-            else if (Directory.Exists(fileName))
-                this.ShowContainerProperties(fileName);
-        }
-    }
+           #endregion
+
+               public void Handle(ShowFilePropertiesEvent message)
+               {
+                       if (message == null)
+                               throw new ArgumentNullException("message");
+                       if (String.IsNullOrWhiteSpace(message.FileName) )
+                               throw new ArgumentException("message");
+                       Contract.EndContractBlock();
+
+                       var fileName = message.FileName;
+                       //TODO: Display file properties for non-container folders
+                       if (File.Exists(fileName))
+                               //Retrieve the full name with exact casing. Pithos names are case sensitive                             
+                               ShowFileProperties(FileInfoExtensions.GetProperFilePathCapitalization(fileName));
+                       else if (Directory.Exists(fileName))
+                               //Retrieve the full name with exact casing. Pithos names are case sensitive
+                       {
+                               var path = FileInfoExtensions.GetProperDirectoryCapitalization(fileName);
+                               if (IsContainer(path))
+                                       ShowContainerProperties(path);
+                               else
+                                       ShowFileProperties(path);
+                       }
+               }
+
+               private bool IsContainer(string path)
+               {
+                       var matchingFolders = from account in _accounts
+                                                                 from rootFolder in Directory.GetDirectories(account.AccountPath)
+                                                                 where rootFolder.Equals(path, StringComparison.InvariantCultureIgnoreCase)
+                                                                 select rootFolder;
+                       return matchingFolders.Any();
+               }
+
+               public FileStatus GetFileStatus(string localFileName)
+               {
+                       if (String.IsNullOrWhiteSpace(localFileName))
+                               throw new ArgumentNullException("localFileName");
+                       Contract.EndContractBlock();
+                       
+                       var statusKeeper = IoC.Get<IStatusKeeper>();
+                       var status=statusKeeper.GetFileStatus(localFileName);
+                       return status;
+               }
+
+           public void RemoveAccountFromDatabase(AccountSettings account)
+           {
+            var statusKeeper = IoC.Get<IStatusKeeper>();
+            statusKeeper.ClearFolderStatus(account.RootPath);          
+           }
+       }
 }