commit 6bcdd8e2ecbe77b8774549324d99d86999770556
Author: Panagiotis Kanavos <pkanavos@gmail.com>
Date:   Sat Mar 3 14:49:02 2012 +0200

    Logging improvements

diff --git a/trunk/Pithos.Client.WPF/Shell/ShellViewModel.cs b/trunk/Pithos.Client.WPF/Shell/ShellViewModel.cs
index eb355a5..a12f712 100644
--- a/trunk/Pithos.Client.WPF/Shell/ShellViewModel.cs
+++ b/trunk/Pithos.Client.WPF/Shell/ShellViewModel.cs
@@ -87,6 +87,7 @@ namespace Pithos.Client.WPF {
 	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 readonly IStatusChecker _statusChecker;
@@ -170,6 +171,7 @@ namespace Pithos.Client.WPF {
 				Log.Error("Error while starting the ShellViewModel",exc);
 				throw;
 			}
+
 		}
 
 
@@ -832,6 +834,18 @@ namespace Pithos.Client.WPF {
 				notification.Message = "Start Synchronisation";
 			}
 
+		    var progress = notification as ProgressNotification;
+		    if (progress != null)
+		    {
+		        StatusMessage = String.Format("Pithos {0}\r\n{1} {2} of {3} - {4}",
+		                                      _fileVersion.Value.FileVersion, 
+                                              progress.Action,
+		                                      progress.Block/(double)progress.TotalBlocks,
+		                                      progress.FileSize.ToByteSize(),
+		                                      progress.FileName);
+		        return;
+		    }
+
 			if (String.IsNullOrWhiteSpace(notification.Message) && String.IsNullOrWhiteSpace(notification.Title))
 				return;
 
diff --git a/trunk/Pithos.Client.WPF/app.config b/trunk/Pithos.Client.WPF/app.config
index 19670ae..e445dde 100644
--- a/trunk/Pithos.Client.WPF/app.config
+++ b/trunk/Pithos.Client.WPF/app.config
@@ -1,172 +1,186 @@
 ﻿<?xml version="1.0" encoding="utf-8"?>
 <configuration>
-  <configSections>
-    <sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
-      <section name="Pithos.Client.WPF.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
-    </sectionGroup>
-    <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
-      <section name="Pithos.Client.WPF.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
-    </sectionGroup>
-    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
-  </configSections>
-  <runtime>
-    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
-      <dependentAssembly>
-        <assemblyIdentity name="Caliburn.Micro" publicKeyToken="8e5891231f2ed21f" culture="neutral" />
-        <bindingRedirect oldVersion="0.0.0.0-1.2.0.0" newVersion="1.2.0.0" />
-      </dependentAssembly>
-      <dependentAssembly>
-        <assemblyIdentity name="NHibernate" publicKeyToken="aa95f207798dfdb4" culture="neutral" />
-        <bindingRedirect oldVersion="0.0.0.0-3.1.0.4000" newVersion="3.1.0.4000" />
-      </dependentAssembly>
-      <dependentAssembly>
-        <assemblyIdentity name="NHibernate.ByteCode.Castle" publicKeyToken="aa95f207798dfdb4" culture="neutral" />
-        <bindingRedirect oldVersion="0.0.0.0-3.1.0.4000" newVersion="3.1.0.4000" />
-      </dependentAssembly>
-    </assemblyBinding>
-  </runtime>
-  <userSettings>
-    <Pithos.Client.WPF.Properties.Settings>
-      <setting name="PithosPath" serializeAs="String">
-        <value>e:\Pithos</value>
-      </setting>
-      <setting name="IconPath" serializeAs="String">
-        <value>C:\Program Files\Common Files\TortoiseOverlays\icons\XPStyle</value>
-      </setting>
-      <setting name="ProxyServer" serializeAs="String">
-        <value />
-      </setting>
-      <setting name="ProxyPort" serializeAs="String">
-        <value>8080</value>
-      </setting>
-      <setting name="ProxyUsername" serializeAs="String">
-        <value />
-      </setting>
-      <setting name="ProxyPassword" serializeAs="String">
-        <value />
-      </setting>
-      <setting name="ProxyAuthentication" serializeAs="String">
-        <value>True</value>
-      </setting>
-      <setting name="ExtensionsActivated" serializeAs="String">
-        <value>True</value>
-      </setting>
-      <setting name="UserName" serializeAs="String">
-        <value>pkanavos</value>
-      </setting>
-      <setting name="ApiKey" serializeAs="String">
-        <value>9d3cb7b231e96f72ebe96af1c6cd5112</value>
-      </setting>
-      <setting name="ShowDesktopNotifications" serializeAs="String">
-        <value>True</value>
-      </setting>
-      <setting name="StartOnSystemStartup" serializeAs="String">
-        <value>True</value>
-      </setting>
-      <setting name="UseNoProxy" serializeAs="String">
-        <value>False</value>
-      </setting>
-      <setting name="UseDefaultProxy" serializeAs="String">
-        <value>True</value>
-      </setting>
-      <setting name="UseManualProxy" serializeAs="String">
-        <value>False</value>
-      </setting>
-      <setting name="MustUpgrade" serializeAs="String">
-        <value>True</value>
-      </setting>
-      <setting name="PollingInterval" serializeAs="String">
-        <value>10</value>
-      </setting>
-      <setting name="ProxyDomain" serializeAs="String">
-        <value />
-      </setting>
-      <setting name="HashingParallelism" serializeAs="String">
-        <value>1</value>
-      </setting>
-      <setting name="StartupDelay" serializeAs="String">
-        <value>00:01:00</value>
-      </setting>
-    </Pithos.Client.WPF.Properties.Settings>
-  </userSettings>
-  <connectionStrings>
-    <add name="Sqlite_InMemory" providerName="System.Data.SQLite" connectionString="Data Source=:memory:;Version=3;New=True" />
-  </connectionStrings>
-  <system.data>
-    <DbProviderFactories>
-      <remove invariant="System.Data.SQLite" />
-      <add name="SQLite Data Provider" invariant="System.Data.SQLite" description=".Net Framework Data Provider for SQLite" type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite" />
-    </DbProviderFactories>
-  </system.data>
+	<configSections>
+		<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+			<section name="Pithos.Client.WPF.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
+		</sectionGroup>
+		<sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+			<section name="Pithos.Client.WPF.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
+		</sectionGroup>
+		<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
+	</configSections>
+	<runtime>
+		<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
+			<dependentAssembly>
+				<assemblyIdentity name="Caliburn.Micro" publicKeyToken="8e5891231f2ed21f" culture="neutral" />
+				<bindingRedirect oldVersion="0.0.0.0-1.2.0.0" newVersion="1.2.0.0" />
+			</dependentAssembly>
+			<dependentAssembly>
+				<assemblyIdentity name="NHibernate" publicKeyToken="aa95f207798dfdb4" culture="neutral" />
+				<bindingRedirect oldVersion="0.0.0.0-3.1.0.4000" newVersion="3.1.0.4000" />
+			</dependentAssembly>
+			<dependentAssembly>
+				<assemblyIdentity name="NHibernate.ByteCode.Castle" publicKeyToken="aa95f207798dfdb4" culture="neutral" />
+				<bindingRedirect oldVersion="0.0.0.0-3.1.0.4000" newVersion="3.1.0.4000" />
+			</dependentAssembly>
+		</assemblyBinding>
+	</runtime>
+	<userSettings>
+		<Pithos.Client.WPF.Properties.Settings>
+			<setting name="PithosPath" serializeAs="String">
+				<value>e:\Pithos</value>
+			</setting>
+			<setting name="IconPath" serializeAs="String">
+				<value>C:\Program Files\Common Files\TortoiseOverlays\icons\XPStyle</value>
+			</setting>
+			<setting name="ProxyServer" serializeAs="String">
+				<value />
+			</setting>
+			<setting name="ProxyPort" serializeAs="String">
+				<value>8080</value>
+			</setting>
+			<setting name="ProxyUsername" serializeAs="String">
+				<value />
+			</setting>
+			<setting name="ProxyPassword" serializeAs="String">
+				<value />
+			</setting>
+			<setting name="ProxyAuthentication" serializeAs="String">
+				<value>True</value>
+			</setting>
+			<setting name="ExtensionsActivated" serializeAs="String">
+				<value>True</value>
+			</setting>
+			<setting name="UserName" serializeAs="String">
+				<value>pkanavos</value>
+			</setting>
+			<setting name="ApiKey" serializeAs="String">
+				<value>9d3cb7b231e96f72ebe96af1c6cd5112</value>
+			</setting>
+			<setting name="ShowDesktopNotifications" serializeAs="String">
+				<value>True</value>
+			</setting>
+			<setting name="StartOnSystemStartup" serializeAs="String">
+				<value>True</value>
+			</setting>
+			<setting name="UseNoProxy" serializeAs="String">
+				<value>False</value>
+			</setting>
+			<setting name="UseDefaultProxy" serializeAs="String">
+				<value>True</value>
+			</setting>
+			<setting name="UseManualProxy" serializeAs="String">
+				<value>False</value>
+			</setting>
+			<setting name="MustUpgrade" serializeAs="String">
+				<value>True</value>
+			</setting>
+			<setting name="PollingInterval" serializeAs="String">
+				<value>10</value>
+			</setting>
+			<setting name="ProxyDomain" serializeAs="String">
+				<value />
+			</setting>
+			<setting name="HashingParallelism" serializeAs="String">
+				<value>1</value>
+			</setting>
+			<setting name="StartupDelay" serializeAs="String">
+				<value>00:01:00</value>
+			</setting>
+		</Pithos.Client.WPF.Properties.Settings>
+	</userSettings>
+	<connectionStrings>
+		<add name="Sqlite_InMemory" providerName="System.Data.SQLite" connectionString="Data Source=:memory:;Version=3;New=True" />
+	</connectionStrings>
+	<system.data>
+		<DbProviderFactories>
+			<remove invariant="System.Data.SQLite" />
+			<add name="SQLite Data Provider" invariant="System.Data.SQLite" description=".Net Framework Data Provider for SQLite" type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite" />
+		</DbProviderFactories>
+	</system.data>
 
-  <startup useLegacyV2RuntimeActivationPolicy="true">
-    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0,Profile=Client" />
-  </startup>
-  <applicationSettings>
-    <Pithos.Client.WPF.Properties.Settings>
-      <setting name="CloudfilesAuthenticationUrl" serializeAs="String">
-        <value>https://auth.api.rackspacecloud.com</value>
-      </setting>
-      <setting name="PithosLoginUrl" serializeAs="String">
-        <value>https://pithos.dev.grnet.gr/login</value>
-      </setting>
-      <setting name="FeedbackUri" serializeAs="String">
-        <value>https://pithos.dev.grnet.gr/im/feedback</value>
-      </setting>
-      <setting name="ProductionServer" serializeAs="String">
-        <value>https://plus.pithos.grnet.gr</value>
-      </setting>
-      <setting name="DevelopmentServer" serializeAs="String">
-        <value>https://pithos.dev.grnet.gr</value>
-      </setting>
-    </Pithos.Client.WPF.Properties.Settings>
-  </applicationSettings>
-  <log4net>
-    <appender name="TraceAppender" type="log4net.Appender.TraceAppender" >
-      <layout type="log4net.Layout.PatternLayout">
-        <conversionPattern value="%logger (%property{myContext}) [%level]- %message%newline" />
-      </layout>
-      <filter type="log4net.Filter.LevelRangeFilter">
-        <levelMin value="INFO" />
-        <levelMax value="FATAL" />
-      </filter>
-    </appender>
-    	
-	<appender name="DumpFileAppender" type="log4net.Appender.RollingFileAppender">
-		<file value="log.txt" />
-		<appendToFile value="false" />
-		<rollingStyle value="Size" />
-		<maxSizeRollBackups value="10" />
-		<maximumFileSize value="100KB" />
-		<staticLogFileName value="true" />
-		<layout type="log4net.Layout.XMLLayout"/>
-	</appender>
+	<startup useLegacyV2RuntimeActivationPolicy="true">
+		<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0,Profile=Client" />
+	</startup>
+	<applicationSettings>
+		<Pithos.Client.WPF.Properties.Settings>
+			<setting name="CloudfilesAuthenticationUrl" serializeAs="String">
+				<value>https://auth.api.rackspacecloud.com</value>
+			</setting>
+			<setting name="PithosLoginUrl" serializeAs="String">
+				<value>https://pithos.dev.grnet.gr/login</value>
+			</setting>
+			<setting name="FeedbackUri" serializeAs="String">
+				<value>https://pithos.dev.grnet.gr/im/feedback</value>
+			</setting>
+			<setting name="ProductionServer" serializeAs="String">
+				<value>https://plus.pithos.grnet.gr</value>
+			</setting>
+			<setting name="DevelopmentServer" serializeAs="String">
+				<value>https://pithos.dev.grnet.gr</value>
+			</setting>
+		</Pithos.Client.WPF.Properties.Settings>
+	</applicationSettings>
+	<log4net>
+		<appender name="TraceAppender" type="log4net.Appender.TraceAppender" >
+			<layout type="log4net.Layout.PatternLayout">
+				<conversionPattern value="%logger (%property{CloudFile}) [%level]- %message%newline" />
+			</layout>
+			<filter type="log4net.Filter.LoggerMatchFilter">
+				<loggerToMatch value="NHibernate" />
+				<acceptOnMatch value="false" />
+			</filter>
+			<filter type="log4net.Filter.LoggerMatchFilter">
+				<loggerToMatch value="Caliburn" />
+				<acceptOnMatch value="false" />
+			</filter>
+		</appender>
 
-	<appender name="OutputDebugStringAppender" type="log4net.Appender.OutputDebugStringAppender" >
-    <layout type="log4net.Layout.PatternLayout">
-          <conversionPattern value="%logger (%property{myContext}) [%level]- %message%newline" />
-    </layout>
-</appender>
+		<appender name="DumpFileAppender" type="log4net.Appender.RollingFileAppender">
+			<file value="log.txt" />
+			<appendToFile value="false" />
+			<rollingStyle value="Size" />
+			<maxSizeRollBackups value="10" />
+			<maximumFileSize value="100KB" />
+			<staticLogFileName value="true" />
+			<layout type="log4net.Layout.XMLLayout"/>
+		</appender>
 
-    <appender name="LossyFileAppender" type="log4net.Appender.BufferingForwardingAppender">
-      <filter type="log4net.Filter.LevelRangeFilter">
-        <levelMin value="DEBUG" />
-        <levelMax value="FATAL" />
-      </filter>
-      <bufferSize value="30" />
-      <lossy value="true"/>
-      <evaluator type="log4net.Core.LevelEvaluator">
-        <threshold value="ERROR" />
-      </evaluator>
-      <appender-ref ref="DumpFileAppender" />
-	  
-    </appender>
+		<appender name="OutputDebugStringAppender" type="log4net.Appender.OutputDebugStringAppender" >
+			<layout type="log4net.Layout.PatternLayout">
+				<conversionPattern value="%logger (%property{Operation}) [%level]- %message%newline" />
+			</layout>
+		</appender>
 
-    <root>
-      <level value="DEBUG" />
-	    <appender-ref ref="LossyFileAppender" />
-		<appender-ref ref="TraceAppender" />
-		<appender-ref ref="OutputDebugStringAppender" />
-	</root>
-  </log4net>
+		<appender name="LossyFileAppender" type="log4net.Appender.BufferingForwardingAppender">
+			<filter type="log4net.Filter.LevelRangeFilter">
+				<levelMin value="DEBUG" />
+				<levelMax value="FATAL" />
+			</filter>
+			<bufferSize value="30" />
+			<lossy value="true"/>
+			<evaluator type="log4net.Core.LevelEvaluator">
+				<threshold value="ERROR" />
+			</evaluator>
+			<appender-ref ref="DumpFileAppender" />
+
+		</appender>
+
+		<logger name="NHibernate" additivity="false">
+			<level value="WARN"/>
+			<appender-ref ref="TraceAppender"/>
+		</logger>
+
+		<logger name="Caliburn" additivity="false">
+			<level value="WARN"/>
+			<appender-ref ref="TraceAppender"/>
+		</logger>
+
+		<root>
+			<level value="DEBUG" />
+			<appender-ref ref="LossyFileAppender" />
+			<appender-ref ref="TraceAppender" />
+			<appender-ref ref="OutputDebugStringAppender" />
+		</root>
+	</log4net>
 </configuration>
\ No newline at end of file
diff --git a/trunk/Pithos.Core/Agents/DeleteAgent.cs b/trunk/Pithos.Core/Agents/DeleteAgent.cs
index bf3dc2d..c02eb2f 100644
--- a/trunk/Pithos.Core/Agents/DeleteAgent.cs
+++ b/trunk/Pithos.Core/Agents/DeleteAgent.cs
@@ -111,7 +111,7 @@ namespace Pithos.Core.Agents
 
             var accountInfo = action.AccountInfo;
 
-            using (log4net.ThreadContext.Stacks["NETWORK"].Push("PROCESS"))
+            using (log4net.ThreadContext.Stacks["Operation"].Push("ProcessDelete"))
             {
                 Log.InfoFormat("[ACTION] Start Processing {0}", action);
 
@@ -212,7 +212,7 @@ namespace Pithos.Core.Agents
 
             var fileAgent = GetFileAgent(accountInfo);
 
-            using (ThreadContext.Stacks["DeleteCloudFile"].Push("Delete"))
+            using (ThreadContext.Stacks["Operation"].Push("DeleteCloudFile"))
             {
                 var fileName = cloudFile.RelativeUrlToFilePath(accountInfo.UserName);
                 var info = fileAgent.GetFileSystemInfo(fileName);
diff --git a/trunk/Pithos.Core/Agents/NetworkAgent.cs b/trunk/Pithos.Core/Agents/NetworkAgent.cs
index 077a810..85c40ad 100644
--- a/trunk/Pithos.Core/Agents/NetworkAgent.cs
+++ b/trunk/Pithos.Core/Agents/NetworkAgent.cs
@@ -115,9 +115,8 @@ namespace Pithos.Core.Agents
 
 
 
-            using (log4net.ThreadContext.Stacks["NETWORK"].Push("PROCESS"))
+            using (log4net.ThreadContext.Stacks["Operation"].Push(action.ToString()))
             {                
-                Log.InfoFormat("[ACTION] Start Processing {0}", action);
 
                 var cloudFile = action.CloudFile;
                 var downloadPath = action.GetDownloadPath();
@@ -125,7 +124,7 @@ namespace Pithos.Core.Agents
                 try
                 {
                     _proceedEvent.Reset();
-                    UpdateStatus(PithosStatus.Syncing);
+                    //UpdateStatus(PithosStatus.Syncing);
                     var accountInfo = action.AccountInfo;
 
                     if (action.Action == CloudActionType.DeleteCloud)
@@ -169,7 +168,7 @@ namespace Pithos.Core.Agents
                                 break;
                         }
                     }
-                    Log.InfoFormat("[ACTION] End Processing {0}:{1}->{2}", action.Action, action.LocalFile,
+                    Log.InfoFormat("End Processing {0}:{1}->{2}", action.Action, action.LocalFile,
                                            action.CloudFile.Name);
                 }
                 catch (WebException exc)
@@ -230,20 +229,22 @@ namespace Pithos.Core.Agents
             if (action.CloudFile == null)
                 throw new ArgumentException("The action's cloud file is not specified", "action");
             Contract.EndContractBlock();
+            using (ThreadContext.Stacks["Operation"].Push("RenameLocalFile"))
+            {
 
-            //We assume that the local file already exists, otherwise the poll agent
-            //would have issued a download request
-
-            var currentInfo = action.CloudFile;
-            var previousInfo = action.CloudFile.Previous;
-            var fileAgent = FileAgent.GetFileAgent(accountInfo);
+                //We assume that the local file already exists, otherwise the poll agent
+                //would have issued a download request
 
-            var previousRelativepath = previousInfo.RelativeUrlToFilePath(accountInfo.UserName);
-            var previousFile = fileAgent.GetFileSystemInfo(previousRelativepath);
+                var currentInfo = action.CloudFile;
+                var previousInfo = action.CloudFile.Previous;
+                var fileAgent = FileAgent.GetFileAgent(accountInfo);
 
-            //In every case we need to move the local file first
-            MoveLocalFile(accountInfo, previousFile, fileAgent, currentInfo);
+                var previousRelativepath = previousInfo.RelativeUrlToFilePath(accountInfo.UserName);
+                var previousFile = fileAgent.GetFileSystemInfo(previousRelativepath);
 
+                //In every case we need to move the local file first
+                MoveLocalFile(accountInfo, previousFile, fileAgent, currentInfo);
+            }
         }
 
         private void MoveLocalFile(AccountInfo accountInfo, FileSystemInfo previousFile, FileAgent fileAgent,
@@ -287,41 +288,44 @@ namespace Pithos.Core.Agents
             if (action.CloudFile== null)
                 throw new ArgumentException("The action's cloud file is not specified", "action");
             Contract.EndContractBlock();
+            using (ThreadContext.Stacks["Operation"].Push("SyncFiles"))
+            {
 
-            var localFile = action.LocalFile;
-            var cloudFile = action.CloudFile;
-            var downloadPath=action.LocalFile.GetProperCapitalization();
+                var localFile = action.LocalFile;
+                var cloudFile = action.CloudFile;
+                var downloadPath = action.LocalFile.GetProperCapitalization();
 
-            var cloudHash = cloudFile.Hash.ToLower();
-            var previousCloudHash = cloudFile.PreviousHash.ToLower();
-            var localHash = action.LocalHash.Value.ToLower();
-            var topHash = action.TopHash.Value.ToLower();
+                var cloudHash = cloudFile.Hash.ToLower();
+                var previousCloudHash = cloudFile.PreviousHash.ToLower();
+                var localHash = action.LocalHash.Value.ToLower();
+                var topHash = action.TopHash.Value.ToLower();
 
-            //At this point we know that an object has changed on the server and that a local
-            //file already exists. We need to decide whether the file has only changed on 
-            //the server or there is a conflicting change on the client.
-            //
-            
-            //Not enough to compare only the local hashes (MD5), also have to compare the tophashes            
-            //If any of the hashes match, we are done
-            if ((cloudHash == localHash || cloudHash == topHash))
-            {
-                Log.InfoFormat("Skipping {0}, hashes match",downloadPath);
-                return;
-            }
+                //At this point we know that an object has changed on the server and that a local
+                //file already exists. We need to decide whether the file has only changed on 
+                //the server or there is a conflicting change on the client.
+                //
 
-            //The hashes DON'T match. We need to sync
+                //Not enough to compare only the local hashes (MD5), also have to compare the tophashes            
+                //If any of the hashes match, we are done
+                if ((cloudHash == localHash || cloudHash == topHash))
+                {
+                    Log.InfoFormat("Skipping {0}, hashes match", downloadPath);
+                    return;
+                }
 
-            // If the previous tophash matches the local tophash, the file was only changed on the server. 
-            if (localHash == previousCloudHash)
-            {
-                await DownloadCloudFile(accountInfo, cloudFile, downloadPath);
-            }
-            else
-            {
-                //If the previous and local hash don't match, there was a local conflict
-                //that was not uploaded to the server. We have a conflict
-                ReportConflict(downloadPath);
+                //The hashes DON'T match. We need to sync
+
+                // If the previous tophash matches the local tophash, the file was only changed on the server. 
+                if (localHash == previousCloudHash)
+                {
+                    await DownloadCloudFile(accountInfo, cloudFile, downloadPath);
+                }
+                else
+                {
+                    //If the previous and local hash don't match, there was a local conflict
+                    //that was not uploaded to the server. We have a conflict
+                    ReportConflict(downloadPath);
+                }
             }
         }
 
@@ -413,36 +417,39 @@ namespace Pithos.Core.Agents
             if (action.OldCloudFile==null)
                 throw new ArgumentException("OldCloudFile","action");
             Contract.EndContractBlock();
-            
-            
-            var newFilePath = action.LocalFile.FullName;
-            
-            //How do we handle concurrent renames and deletes/uploads/downloads?
-            //* A conflicting upload means that a file was renamed before it had a chance to finish uploading
-            //  This should never happen as the network agent executes only one action at a time
-            //* A conflicting download means that the file was modified on the cloud. While we can go on and complete
-            //  the rename, there may be a problem if the file is downloaded in blocks, as subsequent block requests for the 
-            //  same name will fail.
-            //  This should never happen as the network agent executes only one action at a time.
-            //* A conflicting delete can happen if the rename was followed by a delete action that didn't have the chance
-            //  to remove the rename from the queue.
-            //  We can probably ignore this case. It will result in an error which should be ignored            
 
-            
-            //The local file is already renamed
-            StatusKeeper.SetFileOverlayStatus(newFilePath, FileOverlayStatus.Modified);
+            using (ThreadContext.Stacks["Operation"].Push("RenameCloudFile"))
+            {
 
+                var newFilePath = action.LocalFile.FullName;
+
+                //How do we handle concurrent renames and deletes/uploads/downloads?
+                //* A conflicting upload means that a file was renamed before it had a chance to finish uploading
+                //  This should never happen as the network agent executes only one action at a time
+                //* A conflicting download means that the file was modified on the cloud. While we can go on and complete
+                //  the rename, there may be a problem if the file is downloaded in blocks, as subsequent block requests for the 
+                //  same name will fail.
+                //  This should never happen as the network agent executes only one action at a time.
+                //* A conflicting delete can happen if the rename was followed by a delete action that didn't have the chance
+                //  to remove the rename from the queue.
+                //  We can probably ignore this case. It will result in an error which should be ignored            
 
-            var account = action.CloudFile.Account ?? accountInfo.UserName;
-            var container = action.CloudFile.Container;
-            
-            var client = new CloudFilesClient(accountInfo);
-            //TODO: What code is returned when the source file doesn't exist?
-            client.MoveObject(account, container, action.OldCloudFile.Name, container, action.CloudFile.Name);
 
-            StatusKeeper.SetFileStatus(newFilePath, FileStatus.Unchanged);
-            StatusKeeper.SetFileOverlayStatus(newFilePath, FileOverlayStatus.Normal);
-            NativeMethods.RaiseChangeNotification(newFilePath);
+                //The local file is already renamed
+                StatusKeeper.SetFileOverlayStatus(newFilePath, FileOverlayStatus.Modified);
+
+
+                var account = action.CloudFile.Account ?? accountInfo.UserName;
+                var container = action.CloudFile.Container;
+
+                var client = new CloudFilesClient(accountInfo);
+                //TODO: What code is returned when the source file doesn't exist?
+                client.MoveObject(account, container, action.OldCloudFile.Name, container, action.CloudFile.Name);
+
+                StatusKeeper.SetFileStatus(newFilePath, FileStatus.Unchanged);
+                StatusKeeper.SetFileOverlayStatus(newFilePath, FileOverlayStatus.Normal);
+                NativeMethods.RaiseChangeNotification(newFilePath);
+            }
         }
 
         //Download a file.
@@ -461,54 +468,58 @@ namespace Pithos.Core.Agents
             if (!Path.IsPathRooted(filePath))
                 throw new ArgumentException("The filePath must be rooted", "filePath");
             Contract.EndContractBlock();
-            
 
-            var localPath = Interfaces.FileInfoExtensions.GetProperFilePathCapitalization(filePath);
-            var relativeUrl = new Uri(cloudFile.Name, UriKind.Relative);
-
-            var url = relativeUrl.ToString();
-            if (cloudFile.Name.EndsWith(".ignore", StringComparison.InvariantCultureIgnoreCase))
-                return;
+            using (ThreadContext.Stacks["Operation"].Push("DownloadCloudFile"))
+            {
 
+                var localPath = Interfaces.FileInfoExtensions.GetProperFilePathCapitalization(filePath);
+                var relativeUrl = new Uri(cloudFile.Name, UriKind.Relative);
 
-            //Are we already downloading or uploading the file? 
-            using (var gate=NetworkGate.Acquire(localPath, NetworkOperation.Downloading))
-            {
-                if (gate.Failed)
+                var url = relativeUrl.ToString();
+                if (cloudFile.Name.EndsWith(".ignore", StringComparison.InvariantCultureIgnoreCase))
                     return;
-                
-                var client = new CloudFilesClient(accountInfo);
-                var account = cloudFile.Account;
-                var container = cloudFile.Container;
 
-                if (cloudFile.Content_Type == @"application/directory")
+
+                //Are we already downloading or uploading the file? 
+                using (var gate = NetworkGate.Acquire(localPath, NetworkOperation.Downloading))
                 {
-                    if (!Directory.Exists(localPath))
-                        Directory.CreateDirectory(localPath);
-                }
-                else
-                {                    
-                    //Retrieve the hashmap from the server
-                    var serverHash = await client.GetHashMap(account, container, url);
-                    //If it's a small file
-                    if (serverHash.Hashes.Count == 1)
-                        //Download it in one go
-                        await
-                            DownloadEntireFileAsync(accountInfo, client, cloudFile, relativeUrl, localPath, serverHash);
-                        //Otherwise download it block by block
-                    else
-                        await DownloadWithBlocks(accountInfo, client, cloudFile, relativeUrl, localPath, serverHash);
+                    if (gate.Failed)
+                        return;
+
+                    var client = new CloudFilesClient(accountInfo);
+                    var account = cloudFile.Account;
+                    var container = cloudFile.Container;
 
-                    if (cloudFile.AllowedTo == "read")
+                    if (cloudFile.Content_Type == @"application/directory")
                     {
-                        var attributes = File.GetAttributes(localPath);
-                        File.SetAttributes(localPath, attributes | FileAttributes.ReadOnly);                        
+                        if (!Directory.Exists(localPath))
+                            Directory.CreateDirectory(localPath);
+                    }
+                    else
+                    {
+                        //Retrieve the hashmap from the server
+                        var serverHash = await client.GetHashMap(account, container, url);
+                        //If it's a small file
+                        if (serverHash.Hashes.Count == 1)
+                            //Download it in one go
+                            await
+                                DownloadEntireFileAsync(accountInfo, client, cloudFile, relativeUrl, localPath,
+                                                        serverHash);
+                            //Otherwise download it block by block
+                        else
+                            await DownloadWithBlocks(accountInfo, client, cloudFile, relativeUrl, localPath, serverHash);
+
+                        if (cloudFile.AllowedTo == "read")
+                        {
+                            var attributes = File.GetAttributes(localPath);
+                            File.SetAttributes(localPath, attributes | FileAttributes.ReadOnly);
+                        }
                     }
-                }
 
-                //Now we can store the object's metadata without worrying about ghost status entries
-                StatusKeeper.StoreInfo(localPath, cloudFile);
-                
+                    //Now we can store the object's metadata without worrying about ghost status entries
+                    StatusKeeper.StoreInfo(localPath, cloudFile);
+
+                }
             }
         }
 
@@ -604,6 +615,7 @@ namespace Pithos.Core.Agents
             //And compare it with the server's hash
             var upHashes = serverHash.GetHashesAsStrings();
             var localHashes = treeHash.HashDictionary;
+            ReportDownloadProgress(Path.GetFileName(localPath),0,upHashes.Length,cloudFile.Bytes);
             for (int i = 0; i < upHashes.Length; i++)
             {
                 //For every non-matching hash
@@ -633,6 +645,7 @@ namespace Pithos.Core.Agents
 
                     Log.InfoFormat("[BLOCK GET] FINISH {0} of {1} for {2}", i, upHashes.Length, localPath);
                 }
+                ReportDownloadProgress(Path.GetFileName(localPath), i, upHashes.Length, cloudFile.Bytes);
             }
 
             //Want to avoid notifications if no changes were made
@@ -652,123 +665,127 @@ namespace Pithos.Core.Agents
             if (action == null)
                 throw new ArgumentNullException("action");           
             Contract.EndContractBlock();
-
-            try
-            {                
-                var accountInfo = action.AccountInfo;
-
-                var fileInfo = action.LocalFile;
-
-                if (fileInfo.Extension.Equals("ignore", StringComparison.InvariantCultureIgnoreCase))
-                    return;
-                
-                var relativePath = fileInfo.AsRelativeTo(accountInfo.AccountPath);
-                if (relativePath.StartsWith(FolderConstants.OthersFolder))
+            using(ThreadContext.Stacks["Operation"].Push("UploadCloudFile"))
+            {
+                try
                 {
-                    var parts = relativePath.Split('\\');
-                    var accountName = parts[1];
-                    var oldName = accountInfo.UserName;
-                    var absoluteUri = accountInfo.StorageUri.AbsoluteUri;
-                    var nameIndex = absoluteUri.IndexOf(oldName, StringComparison.Ordinal);
-                    var root = absoluteUri.Substring(0, nameIndex);
-
-                    accountInfo = new AccountInfo
-                    {
-                        UserName = accountName,
-                        AccountPath = Path.Combine(accountInfo.AccountPath, parts[0], parts[1]),
-                        StorageUri = new Uri(root + accountName),
-                        BlockHash = accountInfo.BlockHash,
-                        BlockSize = accountInfo.BlockSize,
-                        Token = accountInfo.Token
-                    };
-                }
+                    var accountInfo = action.AccountInfo;
 
+                    var fileInfo = action.LocalFile;
 
-                var fullFileName = fileInfo.GetProperCapitalization();
-                using (var gate = NetworkGate.Acquire(fullFileName, NetworkOperation.Uploading))
-                {
-                    //Abort if the file is already being uploaded or downloaded
-                    if (gate.Failed)
+                    if (fileInfo.Extension.Equals("ignore", StringComparison.InvariantCultureIgnoreCase))
                         return;
 
-                    var cloudFile = action.CloudFile;
-                    var account = cloudFile.Account ?? accountInfo.UserName;
-
-                    var client = new CloudFilesClient(accountInfo);                    
-                    //Even if GetObjectInfo times out, we can proceed with the upload            
-                    var info = client.GetObjectInfo(account, cloudFile.Container, cloudFile.Name);
-
-                    //If this is a read-only file, do not upload changes
-                    if (info.AllowedTo == "read")
-                        return;
-                    
-                    //TODO: Check how a directory hash is calculated -> All dirs seem to have the same hash
-                    if (fileInfo is DirectoryInfo)
+                    var relativePath = fileInfo.AsRelativeTo(accountInfo.AccountPath);
+                    if (relativePath.StartsWith(FolderConstants.OthersFolder))
                     {
-                        //If the directory doesn't exist the Hash property will be empty
-                        if (String.IsNullOrWhiteSpace(info.Hash))
-                            //Go on and create the directory
-                            await client.PutObject(account, cloudFile.Container, cloudFile.Name, fullFileName, String.Empty, "application/directory");
+                        var parts = relativePath.Split('\\');
+                        var accountName = parts[1];
+                        var oldName = accountInfo.UserName;
+                        var absoluteUri = accountInfo.StorageUri.AbsoluteUri;
+                        var nameIndex = absoluteUri.IndexOf(oldName, StringComparison.Ordinal);
+                        var root = absoluteUri.Substring(0, nameIndex);
+
+                        accountInfo = new AccountInfo
+                                          {
+                                              UserName = accountName,
+                                              AccountPath = Path.Combine(accountInfo.AccountPath, parts[0], parts[1]),
+                                              StorageUri = new Uri(root + accountName),
+                                              BlockHash = accountInfo.BlockHash,
+                                              BlockSize = accountInfo.BlockSize,
+                                              Token = accountInfo.Token
+                                          };
                     }
-                    else
+
+
+                    var fullFileName = fileInfo.GetProperCapitalization();
+                    using (var gate = NetworkGate.Acquire(fullFileName, NetworkOperation.Uploading))
                     {
+                        //Abort if the file is already being uploaded or downloaded
+                        if (gate.Failed)
+                            return;
 
-                        var cloudHash = info.Hash.ToLower();
+                        var cloudFile = action.CloudFile;
+                        var account = cloudFile.Account ?? accountInfo.UserName;
 
-                        var hash = action.LocalHash.Value;
-                        var topHash = action.TopHash.Value;
+                        var client = new CloudFilesClient(accountInfo);
+                        //Even if GetObjectInfo times out, we can proceed with the upload            
+                        var info = client.GetObjectInfo(account, cloudFile.Container, cloudFile.Name);
 
-                        //If the file hashes match, abort the upload
-                        if (hash == cloudHash || topHash == cloudHash)
-                        {
-                            //but store any metadata changes 
-                            StatusKeeper.StoreInfo(fullFileName, info);
-                            Log.InfoFormat("Skip upload of {0}, hashes match", fullFileName);
+                        //If this is a read-only file, do not upload changes
+                        if (info.AllowedTo == "read")
                             return;
+
+                        //TODO: Check how a directory hash is calculated -> All dirs seem to have the same hash
+                        if (fileInfo is DirectoryInfo)
+                        {
+                            //If the directory doesn't exist the Hash property will be empty
+                            if (String.IsNullOrWhiteSpace(info.Hash))
+                                //Go on and create the directory
+                                await
+                                    client.PutObject(account, cloudFile.Container, cloudFile.Name, fullFileName,
+                                                     String.Empty, "application/directory");
                         }
+                        else
+                        {
+
+                            var cloudHash = info.Hash.ToLower();
+
+                            var hash = action.LocalHash.Value;
+                            var topHash = action.TopHash.Value;
+
+                            //If the file hashes match, abort the upload
+                            if (hash == cloudHash || topHash == cloudHash)
+                            {
+                                //but store any metadata changes 
+                                StatusKeeper.StoreInfo(fullFileName, info);
+                                Log.InfoFormat("Skip upload of {0}, hashes match", fullFileName);
+                                return;
+                            }
 
 
-                        //Mark the file as modified while we upload it
-                        StatusKeeper.SetFileOverlayStatus(fullFileName, FileOverlayStatus.Modified);
-                        //And then upload it
+                            //Mark the file as modified while we upload it
+                            StatusKeeper.SetFileOverlayStatus(fullFileName, FileOverlayStatus.Modified);
+                            //And then upload it
 
-                        //Upload even small files using the Hashmap. The server may already contain
-                        //the relevant block
+                            //Upload even small files using the Hashmap. The server may already contain
+                            //the relevant block
 
-                        //First, calculate the tree hash
-                        var treeHash = await Signature.CalculateTreeHashAsync(fullFileName, accountInfo.BlockSize,
-                                                                              accountInfo.BlockHash, 2);
+                            //First, calculate the tree hash
+                            var treeHash = await Signature.CalculateTreeHashAsync(fullFileName, accountInfo.BlockSize,
+                                                                                  accountInfo.BlockHash, 2);
 
-                        await UploadWithHashMap(accountInfo, cloudFile, fileInfo as FileInfo, cloudFile.Name, treeHash);
+                            await
+                                UploadWithHashMap(accountInfo, cloudFile, fileInfo as FileInfo, cloudFile.Name, treeHash);
+                        }
+                        //If everything succeeds, change the file and overlay status to normal
+                        StatusKeeper.SetFileState(fullFileName, FileStatus.Unchanged, FileOverlayStatus.Normal);
                     }
-                    //If everything succeeds, change the file and overlay status to normal
-                    StatusKeeper.SetFileState(fullFileName, FileStatus.Unchanged, FileOverlayStatus.Normal);
+                    //Notify the Shell to update the overlays
+                    NativeMethods.RaiseChangeNotification(fullFileName);
+                    StatusNotification.NotifyChangedFile(fullFileName);
+                }
+                catch (AggregateException ex)
+                {
+                    var exc = ex.InnerException as WebException;
+                    if (exc == null)
+                        throw ex.InnerException;
+                    if (HandleUploadWebException(action, exc))
+                        return;
+                    throw;
+                }
+                catch (WebException ex)
+                {
+                    if (HandleUploadWebException(action, ex))
+                        return;
+                    throw;
+                }
+                catch (Exception ex)
+                {
+                    Log.Error("Unexpected error while uploading file", ex);
+                    throw;
                 }
-                //Notify the Shell to update the overlays
-                NativeMethods.RaiseChangeNotification(fullFileName);
-                StatusNotification.NotifyChangedFile(fullFileName);
-            }
-            catch (AggregateException ex)
-            {
-                var exc = ex.InnerException as WebException;
-                if (exc == null)
-                    throw ex.InnerException;
-                if (HandleUploadWebException(action, exc)) 
-                    return;
-                throw;
-            }
-            catch (WebException ex)
-            {
-                if (HandleUploadWebException(action, ex))
-                    return;
-                throw;
-            }
-            catch (Exception ex)
-            {
-                Log.Error("Unexpected error while uploading file", ex);
-                throw;
             }
-
         }
 
 
@@ -810,9 +827,11 @@ namespace Pithos.Core.Agents
             var account = cloudFile.Account ?? accountInfo.UserName;
             var container = cloudFile.Container ;
 
-            var client = new CloudFilesClient(accountInfo);
+            var client = new CloudFilesClient(accountInfo);            
             //Send the hashmap to the server            
             var missingHashes =  await client.PutHashMap(account, container, url, treeHash);
+            int block = 0;
+            ReportUploadProgress(fileInfo.Name,block++, missingHashes.Count, fileInfo.Length);
             //If the server returns no missing hashes, we are done
             while (missingHashes.Count > 0)
             {
@@ -834,17 +853,25 @@ namespace Pithos.Core.Agents
                     }
                     catch (Exception exc)
                     {
-                        Log.ErrorFormat("[ERROR] uploading block {0} of {1}\n{2}", blockIndex, fullFileName, exc);
+                        Log.Error(String.Format("Uploading block {0} of {1}", blockIndex, fullFileName), exc);
                     }
-
+                    ReportUploadProgress(fileInfo.Name,block++, missingHashes.Count, fileInfo.Length);
                 }
 
                 //Repeat until there are no more missing hashes                
                 missingHashes = await client.PutHashMap(account, container, url, treeHash);
             }
+            ReportUploadProgress(fileInfo.Name, missingHashes.Count, missingHashes.Count, fileInfo.Length);
         }
 
-
+        private void ReportUploadProgress(string fileName,int block, int totalBlocks, long fileSize)
+        {
+            StatusNotification.Notify(new ProgressNotification(fileName,"Uploading",block,totalBlocks,fileSize) );            
+        }
+        private void ReportDownloadProgress(string fileName,int block, int totalBlocks, long fileSize)
+        {
+            StatusNotification.Notify(new ProgressNotification(fileName,"Downloading",block,totalBlocks,fileSize) );            
+        }
     }
 
    
diff --git a/trunk/Pithos.Core/Agents/PollAgent.cs b/trunk/Pithos.Core/Agents/PollAgent.cs
index 6da6d51..2089da7 100644
--- a/trunk/Pithos.Core/Agents/PollAgent.cs
+++ b/trunk/Pithos.Core/Agents/PollAgent.cs
@@ -352,8 +352,10 @@ namespace Pithos.Core.Agents
                 var deletedFiles = new List<FileSystemInfo>();
                 foreach (var objectInfo in cloudFiles)
                 {
+                    Log.DebugFormat("Handle deleted [{0}]",objectInfo.Uri);
                     var relativePath = objectInfo.RelativeUrlToFilePath(accountInfo.UserName);
                     var item = FileAgent.GetFileAgent(accountInfo).GetFileSystemInfo(relativePath);
+                    Log.DebugFormat("Will delete [{0}] for [{1}]", item.FullName,objectInfo.Uri);
                     if (item.Exists)
                     {
                         if ((item.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
@@ -361,13 +363,19 @@ namespace Pithos.Core.Agents
                             item.Attributes = item.Attributes & ~FileAttributes.ReadOnly;
 
                         }
-                        item.Delete();
+                        var directory = item as DirectoryInfo;
+                        if (directory!=null)
+                            directory.Delete(true);
+                        else
+                            item.Delete();
+                        Log.DebugFormat("Deleted [{0}] for [{1}]", item.FullName, objectInfo.Uri);
                         DateTime lastDate;
                         _lastSeen.TryRemove(item.FullName, out lastDate);
                         deletedFiles.Add(item);
                     }
                     StatusKeeper.SetFileState(item.FullName, FileStatus.Deleted, FileOverlayStatus.Deleted);
                 }
+                Log.InfoFormat("[{0}] files were deleted",deletedFiles.Count);
                 StatusNotification.NotifyForFiles(deletedFiles, String.Format("{0} files were deleted", deletedFiles.Count), TraceLevel.Info);
             }
 
diff --git a/trunk/Pithos.Core/Agents/StatusAgent.cs b/trunk/Pithos.Core/Agents/StatusAgent.cs
index f0d54cb..e9c6e8c 100644
--- a/trunk/Pithos.Core/Agents/StatusAgent.cs
+++ b/trunk/Pithos.Core/Agents/StatusAgent.cs
@@ -254,6 +254,11 @@ namespace Pithos.Core.Agents
                 {
                     //This file has a matching state. Need to check for possible changes
                     var hashString = file.CalculateHash(BlockSize,BlockHash);
+                    //TODO: Need a way to attach the hashes to the filestate so we don't
+                    //recalculate them each time a call to calculate has is made
+                    //We can either store them to the filestate or add them to a 
+                    //dictionary
+
                     //If the hashes don't match the file was changed
                     if (fileState.Checksum != hashString)
                     {
diff --git a/trunk/Pithos.Core/Agents/WorkflowAgent.cs b/trunk/Pithos.Core/Agents/WorkflowAgent.cs
index 79617cb..5fe0cda 100644
--- a/trunk/Pithos.Core/Agents/WorkflowAgent.cs
+++ b/trunk/Pithos.Core/Agents/WorkflowAgent.cs
@@ -70,6 +70,8 @@ namespace Pithos.Core.Agents
         [System.ComponentModel.Composition.Import]
         public NetworkAgent NetworkAgent { get; set; }
 
+        [System.ComponentModel.Composition.Import]
+        public IPithosSettings Settings { get; set; }
 
 
         public WorkflowAgent()
@@ -210,7 +212,7 @@ namespace Pithos.Core.Agents
         {
             
 
-            using (log4net.ThreadContext.Stacks["Workflow"].Push("Restart"))
+            using (log4net.ThreadContext.Stacks["Operation"].Push("RestartInterrupted"))
             {
                 if (Log.IsDebugEnabled)
                     Log.Debug("Starting interrupted files");
@@ -219,13 +221,14 @@ namespace Pithos.Core.Agents
                     .ToLower();
 
 
-
+                
+                
                 var account = accountInfo;
                 var pendingEntries = (from state in FileState.Queryable
                                      where state.FileStatus != FileStatus.Unchanged &&
                                            !state.FilePath.StartsWith(cachePath) &&
                                            !state.FilePath.EndsWith(".ignore") &&
-                                           state.FilePath.StartsWith(account.AccountPath)
+                                           state.FilePath.StartsWith(account.AccountPath)                                            
                                      select state).ToList();
                 if (pendingEntries.Count>0)
                     StatusNotification.NotifyChange("Restart processing interrupted files", TraceLevel.Verbose);
diff --git a/trunk/Pithos.Core/IStatusNotification.cs b/trunk/Pithos.Core/IStatusNotification.cs
index 196973f..f2fb632 100644
--- a/trunk/Pithos.Core/IStatusNotification.cs
+++ b/trunk/Pithos.Core/IStatusNotification.cs
@@ -71,6 +71,23 @@ namespace Pithos.Core
         }
     }
 
+    public class ProgressNotification:Notification
+    {
+        public string FileName { get; set; }
+        public int Block { get; set; }
+        public int TotalBlocks { get; set; }
+        public long FileSize { get; set; }
+        public string Action { get; set; }
+        public ProgressNotification(string fileName, string action,int block, int totalBlocks, long fileSize)
+        {
+            FileName = fileName;
+            Block = block;
+            TotalBlocks = totalBlocks;
+            FileSize = fileSize;
+            Action = action;
+        }
+    }
+
     public class Notification<T>:Notification
     {
         public T Data { get; set; }
diff --git a/trunk/Pithos.Core/PithosMonitor.cs b/trunk/Pithos.Core/PithosMonitor.cs
index 567be26..b366804 100644
--- a/trunk/Pithos.Core/PithosMonitor.cs
+++ b/trunk/Pithos.Core/PithosMonitor.cs
@@ -269,7 +269,7 @@ namespace Pithos.Core
         private void IndexLocalFiles()
         {
             StatusNotification.NotifyChange("Indexing Local Files");
-            using (ThreadContext.Stacks["Monitor"].Push("Indexing local files"))
+            using (ThreadContext.Stacks["Operation"].Push("Indexing local files"))
             {
                 Log.Info("START");
                 try
diff --git a/trunk/Pithos.Interfaces/ObjectInfo.cs b/trunk/Pithos.Interfaces/ObjectInfo.cs
index 39105ad..fb11d4c 100644
--- a/trunk/Pithos.Interfaces/ObjectInfo.cs
+++ b/trunk/Pithos.Interfaces/ObjectInfo.cs
@@ -139,7 +139,9 @@ namespace Pithos.Interfaces
             get
             {
                 var relativeUrl=String.Format("{0}/{1}/{2}",Account, Container,Name);
-                return new Uri(StorageUri,relativeUrl);
+                return StorageUri==null 
+                    ? new Uri(relativeUrl,UriKind.Relative) 
+                    : new Uri(StorageUri, relativeUrl);
             }
         }
 
diff --git a/trunk/Pithos.Network/BlockHashAlgorithms.cs b/trunk/Pithos.Network/BlockHashAlgorithms.cs
index a8de93b..150e052 100644
--- a/trunk/Pithos.Network/BlockHashAlgorithms.cs
+++ b/trunk/Pithos.Network/BlockHashAlgorithms.cs
@@ -42,6 +42,7 @@
 using System.Collections.Concurrent;
 using System.Diagnostics.Contracts;
 using System.IO;
+using System.Reflection;
 using System.Security.Cryptography;
 using System.Threading.Tasks;
 using System.Threading.Tasks.Dataflow;
@@ -58,6 +59,8 @@ namespace Pithos.Network
     /// </summary>
     public static class BlockHashAlgorithms
     {
+        private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
         public static Func<FileStream, int, string, ConcurrentDictionary<int, byte[]>, int, Task<ConcurrentDictionary<int, byte[]>>> CalculateBlockHash;
 
         public static Task<ConcurrentDictionary<int, byte[]>> CalculateBlockHashesRecursiveAsync(FileStream stream, int blockSize, string algorithm, ConcurrentDictionary<int, byte[]> hashes = null, int index = 0)
@@ -143,6 +146,11 @@ namespace Pithos.Network
 
             var hashes = new ConcurrentDictionary<int, byte[]>();
 
+            var path = stream.Name;
+            var size = stream.Length;
+            Log.DebugFormat("Hashing [{0}] size [{1}]",path,size);
+            
+/*
             var options = new ExecutionDataflowBlockOptions {BoundedCapacity = parallelism,MaxDegreeOfParallelism=parallelism};
             var hashBlock=new ActionBlock<Tuple<int,byte[]>>(input=>
                               {
@@ -158,20 +166,39 @@ namespace Pithos.Network
                                       hashes[idx] = hash;
                                   }                                  
                               },options);
+*/
 
             var buffer = new byte[blockSize];
             int read;
             int index = 0;
-            while ((read = await stream.ReadAsync(buffer, 0, blockSize)) > 0)
+            using (var hasher = HashAlgorithm.Create(algorithm))
             {
-                var block = new byte[read];
-                Buffer.BlockCopy(buffer,0,block,0,read);
-                await hashBlock.SendAsync(Tuple.Create(index, block));
-                index += read;
-            };
+                while ((read = await stream.ReadAsync(buffer, 0, blockSize)) > 0)
+                {
+                    //                var block = new byte[read];
+
+                    //This code was added for compatibility with the way Pithos calculates the last hash
+                    //We calculate the hash only up to the last non-null byte
+                    var lastByteIndex = Array.FindLastIndex(buffer, read - 1, aByte => aByte != 0);
+
+                    var hash = hasher.ComputeHash(buffer, 0, lastByteIndex + 1);
+                    Log.DebugFormat("Hashed [{0}] [{1}/{2}] [{3:p}]", path, index,size,(double)index/size);
+                    hashes[index] = hash;
+                    index += read;
+                }
+
+                /*
+                                Buffer.BlockCopy(buffer,0,block,0,read);
+                                await hashBlock.SendAsync(Tuple.Create(index, block));
+                */
+                
+            }
+            
 
+/*
             hashBlock.Complete();
             await hashBlock.Completion;
+*/
 
             return hashes;
         }
diff --git a/trunk/Pithos.Network/CloudFilesClient.cs b/trunk/Pithos.Network/CloudFilesClient.cs
index fb5ba71..bd22abb 100644
--- a/trunk/Pithos.Network/CloudFilesClient.cs
+++ b/trunk/Pithos.Network/CloudFilesClient.cs
@@ -46,7 +46,6 @@
 
 
 using System;
-using System.Collections;
 using System.Collections.Generic;
 using System.Collections.Specialized;
 using System.ComponentModel.Composition;
diff --git a/trunk/Pithos.Network/RestClient.cs b/trunk/Pithos.Network/RestClient.cs
index 5c1a77b..ac96cd6 100644
--- a/trunk/Pithos.Network/RestClient.cs
+++ b/trunk/Pithos.Network/RestClient.cs
@@ -147,8 +147,8 @@ namespace Pithos.Network
 
         //Asynchronous version
         protected override WebResponse GetWebResponse(WebRequest request, IAsyncResult result)
-        {
-            Log.InfoFormat("ASYNC [{0}] {1}",request.Method, request.RequestUri);
+        {            
+            Log.InfoFormat("[{0}] {1}", request.Method, request.RequestUri); 
             HttpWebResponse response = null;
 
             try
@@ -157,6 +157,7 @@ namespace Pithos.Network
             }
             catch (WebException exc)
             {
+                Log.WarnFormat("[{0}] {1} {2}", request.Method, exc.Status, request.RequestUri);      
                 if (!TryGetResponse(exc, out response))
                     throw;
             }
@@ -174,11 +175,13 @@ namespace Pithos.Network
         {
             HttpWebResponse response = null;
             try
-            {                                
+            {           
+                Log.InfoFormat("[{0}] {1}",request.Method,request.RequestUri);     
                 response = (HttpWebResponse)base.GetWebResponse(request);
             }
             catch (WebException exc)
             {
+                Log.WarnFormat("[{0}] {1} {2}", request.Method, exc.Status,  request.RequestUri);     
                 if (!TryGetResponse(exc, out response))
                     throw;
             }
diff --git a/trunk/Pithos.Network/Signature.cs b/trunk/Pithos.Network/Signature.cs
index 9fae806..296e810 100644
--- a/trunk/Pithos.Network/Signature.cs
+++ b/trunk/Pithos.Network/Signature.cs
@@ -44,6 +44,7 @@ using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Diagnostics.Contracts;
 using System.IO;
+using System.Reflection;
 using System.Runtime.Remoting.Metadata.W3cXsd2001;
 using System.Security.Cryptography;
 using System.Threading.Tasks;
@@ -53,6 +54,8 @@ namespace Pithos.Network
 {
     public static class Signature
     {
+        private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
         public static string CalculateMD5(FileInfo info)
         {
             if (info==null)
diff --git a/trunk/Pithos.Setup.x64/Pithos.Setup.x64.vdproj b/trunk/Pithos.Setup.x64/Pithos.Setup.x64.vdproj
index 2c38bff..65eb73e 100644
--- a/trunk/Pithos.Setup.x64/Pithos.Setup.x64.vdproj
+++ b/trunk/Pithos.Setup.x64/Pithos.Setup.x64.vdproj
@@ -171,6 +171,12 @@
         }
         "Entry"
         {
+        "MsmKey" = "8:_928344C82AF3698D55890E76C45404A5"
+        "OwnerKey" = "8:_8A9B95E5C0E945ECA7F21580A0D088D9"
+        "MsmSig" = "8:_UNDEFINED"
+        }
+        "Entry"
+        {
         "MsmKey" = "8:_A490E069DF5DE5852575B6E157EB50BE"
         "OwnerKey" = "8:_1BD6A9CD577C40098C968C8B464A03BC"
         "MsmSig" = "8:_UNDEFINED"
@@ -364,6 +370,12 @@
         "Entry"
         {
         "MsmKey" = "8:_UNDEFINED"
+        "OwnerKey" = "8:_928344C82AF3698D55890E76C45404A5"
+        "MsmSig" = "8:_UNDEFINED"
+        }
+        "Entry"
+        {
+        "MsmKey" = "8:_UNDEFINED"
         "OwnerKey" = "8:_8A9B95E5C0E945ECA7F21580A0D088D9"
         "MsmSig" = "8:_UNDEFINED"
         }
@@ -871,6 +883,37 @@
             "IsDependency" = "11:TRUE"
             "IsolateTo" = "8:"
             }
+            "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_928344C82AF3698D55890E76C45404A5"
+            {
+            "AssemblyRegister" = "3:1"
+            "AssemblyIsInGAC" = "11:FALSE"
+            "AssemblyAsmDisplayName" = "8:AppLimit.NetSparkle.Net40, Version=1.0.83.0, Culture=neutral, processorArchitecture=MSIL"
+                "ScatterAssemblies"
+                {
+                    "_928344C82AF3698D55890E76C45404A5"
+                    {
+                    "Name" = "8:AppLimit.NetSparkle.Net40.dll"
+                    "Attributes" = "3:512"
+                    }
+                }
+            "SourcePath" = "8:AppLimit.NetSparkle.Net40.dll"
+            "TargetName" = "8:"
+            "Tag" = "8:"
+            "Folder" = "8:_FA3E4362540D4C76A5914763C178A3BD"
+            "Condition" = "8:"
+            "Transitive" = "11:FALSE"
+            "Vital" = "11:TRUE"
+            "ReadOnly" = "11:FALSE"
+            "Hidden" = "11:FALSE"
+            "System" = "11:FALSE"
+            "Permanent" = "11:FALSE"
+            "SharedLegacy" = "11:FALSE"
+            "PackageAs" = "3:1"
+            "Register" = "3:1"
+            "Exclude" = "11:FALSE"
+            "IsDependency" = "11:TRUE"
+            "IsolateTo" = "8:"
+            }
             "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_A490E069DF5DE5852575B6E157EB50BE"
             {
             "AssemblyRegister" = "3:1"
@@ -1333,7 +1376,7 @@
         "Name" = "8:Microsoft Visual Studio"
         "ProductName" = "8:Pithos"
         "ProductCode" = "8:{57BE17ED-F02F-43C0-B44B-845697CD3D6A}"
-        "PackageCode" = "8:{F55471B6-9395-4893-810B-8199D93F61CD}"
+        "PackageCode" = "8:{A4185E8B-DEC9-4E52-BBE4-B959A8B30DD2}"
         "UpgradeCode" = "8:{205365D1-28AA-4322-A46C-FCB37502C6EF}"
         "AspNetVersion" = "8:4.0.30319.0"
         "RestartWWWService" = "11:FALSE"
@@ -1344,7 +1387,7 @@
         "Manufacturer" = "8:GRNET"
         "ARPHELPTELEPHONE" = "8:"
         "ARPHELPLINK" = "8:http://code.grnet.gr/projects/pithos-ms-client"
-        "Title" = "8:Pithos"
+        "Title" = "8:Pithos [Version]"
         "Subject" = "8:"
         "ARPCONTACT" = "8:GRNet"
         "Keywords" = "8:"
diff --git a/trunk/Pithos.sln b/trunk/Pithos.sln
index 65e36dc..88c5d04 100644
--- a/trunk/Pithos.sln
+++ b/trunk/Pithos.sln
@@ -37,6 +37,12 @@ Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "Pithos.Setup.x64", "Pithos.
 EndProject
 Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "Pithos.Setup.x86", "Pithos.Setup.x86\Pithos.Setup.x86.vdproj", "{0D7E50F2-D7B4-4458-AA01-2CAC0F386737}"
 EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Pithos.AppCast", "Pithos.AppCast", "{4ACC38DA-F81B-4DA5-A1AB-CEAAF3B7008A}"
+	ProjectSection(SolutionItems) = preProject
+		Pithos.AppCast\rnotes.css = Pithos.AppCast\rnotes.css
+		Pithos.AppCast\versioninfo.xml = Pithos.AppCast\versioninfo.xml
+	EndProjectSection
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug All|Any CPU = Debug All|Any CPU
