Revision 437abfca

b/trunk/Pithos.Client.WPF/Pithos.Client.WPF.csproj
133 133
    <CodeAnalysisRuleDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules</CodeAnalysisRuleDirectories>
134 134
  </PropertyGroup>
135 135
  <ItemGroup>
136
    <Reference Include="AsyncCtpLibrary, Version=1.0.4107.18181, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
137
      <HintPath>C:\Users\Administrator\Documents\Microsoft Visual Studio Async CTP\Samples\AsyncCtpLibrary.dll</HintPath>
138
    </Reference>
136 139
    <Reference Include="Caliburn.Micro, Version=1.2.0.0, Culture=neutral, PublicKeyToken=8e5891231f2ed21f, processorArchitecture=MSIL">
137 140
      <HintPath>..\Libraries\Caliburn.Micro.dll</HintPath>
138 141
    </Reference>
b/trunk/Pithos.Client.WPF/PithosAccount.cs
75 75
                            var query = request.QueryString;
76 76
                            var userName = query["user"];
77 77
                            var token = query["token"];
78

  
79
                            Log.InfoFormat("[RETRIEVE] Credentials retrieved user:{0} token:{1}", userName, token);
80
                            Respond(context);
81
                            
82
                            return new NetworkCredential(userName, token);
78
                            if (String.IsNullOrWhiteSpace(userName) || String.IsNullOrWhiteSpace(token))
79
                            {
80
                                Respond(context, "Failure", "The server did not return a username or token");
81
                                Log.ErrorFormat("[RETRIEVE] No credentials returned by server");
82
                                throw new Exception("The server did not return a username or token");
83
                            }
84
                            else
85
                            {
86

  
87
                                Log.InfoFormat("[RETRIEVE] Credentials retrieved user:{0} token:{1}", userName, token);
88
                                Respond(context, "Authenticated", "Got It");
89

  
90
                                return new NetworkCredential(userName, token);
91
                            }
83 92
                        }
84 93

  
85 94
                    }
......
100 109
            return receiveCredentials;
101 110
        }
102 111

  
103
        private static void Respond(HttpListenerContext context)
112
        private static void Respond(HttpListenerContext context,string title,string message)
104 113
        {
105 114
            var response = context.Response;
106
            var outBuffer = Encoding.UTF8.GetBytes("<html><head><title>Authenticated</title></head><body><h1>Got It</h1></body></html>");
115
            var html = String.Format("<html><head><title>{0}</title></head><body><h1>{1}</h1></body></html>", title, message);
116
            var outBuffer = Encoding.UTF8.GetBytes(html);
107 117
            response.ContentLength64 = outBuffer.Length;
108 118
            using (var stream = response.OutputStream)
109 119
            {
b/trunk/Pithos.Client.WPF/Preferences/PreferencesViewModel.cs
228 228
            NotifyOfPropertyChange(()=>Settings);
229 229
        }
230 230

  
231
        public void AddPithosAccount()
231
        public async void AddPithosAccount()
232 232
       {
233
           var task=PithosAccount.RetrieveCredentialsAsync(Settings.PithosSite)
234
               .ContinueWith(t=>
235
                   {                       
236
                       var credentials=t.Result;
237
                       var account = Settings.Accounts.FirstOrDefault(act => act.AccountName == credentials.UserName);
238
                       if (account == null)
239
                       {
240
                           account=new AccountSettings{
241
                               AccountName=credentials.UserName,
242
                               ApiKey=credentials.Password,
243
                               UsePithos=true
244
                           };
245
                           Settings.Accounts.Add(account);
246
                           (Accounts as IProducerConsumerCollection<AccountSettings>).TryAdd(account);
247
                       }
248
                       else
249
                       {
250
                           account.ApiKey=credentials.Password;
251
                       }
252
                       //SelectedAccountIndex= Settings.Accounts.IndexOf(account);
253
                       CurrentAccount = account;
254
                       NotifyOfPropertyChange(() => Accounts);
255
                       NotifyOfPropertyChange(()=>Settings);                       
256
                   });
257
            ((Task)task).WaitWithPumping();
233
            var credentials=await PithosAccount.RetrieveCredentialsAsync(Settings.PithosSite);
234
            var account = Settings.Accounts.FirstOrDefault(act => act.AccountName == credentials.UserName);
235
            if (account == null)
236
            {
237
                account=new AccountSettings{
238
                    AccountName=credentials.UserName,
239
                    ApiKey=credentials.Password,
240
                    UsePithos=true
241
                };
242
                Settings.Accounts.Add(account);
243
                (Accounts as IProducerConsumerCollection<AccountSettings>).TryAdd(account);
244
            }
245
            else
246
            {
247
                account.ApiKey=credentials.Password;
248
            }
249
            //SelectedAccountIndex= Settings.Accounts.IndexOf(account);
250
            CurrentAccount = account;
251
            NotifyOfPropertyChange(() => Accounts);
252
            NotifyOfPropertyChange(()=>Settings);                       
258 253
       }
259 254

  
260 255
        public void RemoveAccount()
b/trunk/Pithos.Client.WPF/ShellViewModel.cs
422 422
                        {
423 423
                            var message = String.Format("API Key Expired for {0}. Starting Renewal",monitor.UserName);                            
424 424
                            Log.Error(message,exc);
425
                            TryAuthorize(monitor,retries);
425
                            TryAuthorize(monitor,retries).Wait();
426 426
                        }
427 427
                        else
428 428
                        {
b/trunk/Pithos.Core.Test/NetworkAgentTest.cs
15 15
    class NetworkAgentTest
16 16
    {
17 17
        [Test]
18
        public void TestUpload()
18
        public async void TestUpload()
19 19
        {
20 20
            var agent = new NetworkAgent();
21 21

  
......
50 50

  
51 51
            client.DeleteObject(null, FolderConstants.PithosContainer, fileName);
52 52

  
53
            var task = Signature.CalculateTreeHashAsync(filePath, accountInfo.BlockSize, accountInfo.BlockHash);
53
            var treeHash = await Signature.CalculateTreeHashAsync(filePath, accountInfo.BlockSize, accountInfo.BlockHash);
54 54
            var cloudFile = new ObjectInfo {Account = account, Container = "pithos"};
55 55
            var fileInfo = new FileInfo(filePath);
56 56

  
57
            var tasks=agent.UploadWithHashMap(accountInfo,cloudFile,fileInfo,fileName,task);
58
            Task.Factory.Iterate(tasks).Wait();
59

  
60
            var newHash = client.GetHashMap(null, FolderConstants.PithosContainer, fileName).Result;
57
            agent.UploadWithHashMap(accountInfo,cloudFile,fileInfo,fileName,treeHash);
58
            
59
            var newHash = await client.GetHashMap(null, FolderConstants.PithosContainer, fileName);
61 60

  
62 61

  
63
            var treeHash = task.Result;
62
            
64 63
            Assert.AreEqual(treeHash.TopHash, newHash.TopHash);
65 64

  
66 65
            Assert.AreEqual(treeHash.Hashes, newHash.Hashes);
b/trunk/Pithos.Core.Test/Pithos.Core.Test.csproj
101 101
    <CodeAnalysisRuleDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules</CodeAnalysisRuleDirectories>
102 102
  </PropertyGroup>
103 103
  <ItemGroup>
104
    <Reference Include="AsyncCtpLibrary, Version=1.0.4107.18181, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
105
      <HintPath>C:\Users\Administrator\Documents\Microsoft Visual Studio Async CTP\Samples\AsyncCtpLibrary.dll</HintPath>
106
    </Reference>
104 107
    <Reference Include="Castle.ActiveRecord, Version=3.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL" />
105 108
    <Reference Include="Castle.Core">
106 109
      <HintPath>..\Libraries\Castle.Core.dll</HintPath>
b/trunk/Pithos.Core/Agents/NetworkAgent.cs
782 782
        }
783 783

  
784 784

  
785
        private void UploadCloudFile(CloudAction action)
785
        private async void UploadCloudFile(CloudAction action)
786 786
        {
787 787
            if (action == null)
788 788
                throw new ArgumentNullException("action");           
......
790 790

  
791 791
            try
792 792
            {
793
                var upload = Task.Factory.Iterate(UploadIterator(action));
794
                upload.Wait();
795
            }                
796
            catch (AggregateException ex)
797
            {                
798
                var exc = ex.InnerException as WebException;
799
                if (exc==null)
800
                    throw ex.InnerException;
801
                var response = exc.Response as HttpWebResponse;
802
                if (response==null)
803
                    throw exc;
804
                if (response.StatusCode == HttpStatusCode.Unauthorized)
805
                {
806
                    Log.Error("Not allowed to upload file", exc);
807
                    var message = String.Format("Not allowed to uplad file {0}",action.LocalFile.FullName);
808
                    StatusKeeper.SetFileState(action.LocalFile.FullName,FileStatus.Unchanged,FileOverlayStatus.Normal);
809
                    StatusNotification.NotifyChange(message,TraceLevel.Warning);
810
                    return;
811
                }
812
                throw;
813
            }
814
        }
793
                if (action == null)
794
                    throw new ArgumentNullException("action");            
795
                Contract.EndContractBlock();
815 796

  
816
        private IEnumerable<Task> UploadIterator(CloudAction action)
817
        {
818
            if (action == null)
819
                throw new ArgumentNullException("action");            
820
            Contract.EndContractBlock();
821

  
822
            var accountInfo=action.AccountInfo;
797
                var accountInfo=action.AccountInfo;
823 798
            
824
            var fileInfo=action.LocalFile;                        
799
                var fileInfo=action.LocalFile;                        
825 800

  
826
            if (fileInfo.Extension.Equals("ignore", StringComparison.InvariantCultureIgnoreCase))
827
                yield break;
801
                if (fileInfo.Extension.Equals("ignore", StringComparison.InvariantCultureIgnoreCase))
802
                    return;
828 803

  
829
            var relativePath = fileInfo.AsRelativeTo(accountInfo.AccountPath);
830
            if (relativePath.StartsWith(FolderConstants.OthersFolder))
831
            {
832
                var parts=relativePath.Split('\\');
833
                var accountName = parts[1];
834
                var oldName = accountInfo.UserName;
835
                var absoluteUri = accountInfo.StorageUri.AbsoluteUri;
836
                var nameIndex=absoluteUri.IndexOf(oldName);
837
                var root=absoluteUri.Substring(0, nameIndex);
838

  
839
                accountInfo = new AccountInfo
804
                var relativePath = fileInfo.AsRelativeTo(accountInfo.AccountPath);
805
                if (relativePath.StartsWith(FolderConstants.OthersFolder))
840 806
                {
841
                    UserName = accountName,
842
                    AccountPath = Path.Combine(accountInfo.AccountPath, parts[0], parts[1]),
843
                    StorageUri = new Uri(root + accountName),
844
                    BlockHash=accountInfo.BlockHash,
845
                    BlockSize=accountInfo.BlockSize,
846
                    Token=accountInfo.Token
847
                };
848
            }
807
                    var parts=relativePath.Split('\\');
808
                    var accountName = parts[1];
809
                    var oldName = accountInfo.UserName;
810
                    var absoluteUri = accountInfo.StorageUri.AbsoluteUri;
811
                    var nameIndex=absoluteUri.IndexOf(oldName);
812
                    var root=absoluteUri.Substring(0, nameIndex);
813

  
814
                    accountInfo = new AccountInfo
815
                    {
816
                        UserName = accountName,
817
                        AccountPath = Path.Combine(accountInfo.AccountPath, parts[0], parts[1]),
818
                        StorageUri = new Uri(root + accountName),
819
                        BlockHash=accountInfo.BlockHash,
820
                        BlockSize=accountInfo.BlockSize,
821
                        Token=accountInfo.Token
822
                    };
823
                }
849 824

  
850 825

  
851
            var fullFileName = fileInfo.FullName;
852
            using(var gate=NetworkGate.Acquire(fullFileName,NetworkOperation.Uploading))
853
            {
854
                //Abort if the file is already being uploaded or downloaded
855
                if (gate.Failed)
856
                    yield break;
826
                var fullFileName = fileInfo.FullName;
827
                using(var gate=NetworkGate.Acquire(fullFileName,NetworkOperation.Uploading))
828
                {
829
                    //Abort if the file is already being uploaded or downloaded
830
                    if (gate.Failed)
831
                        return;
857 832

  
858
                var cloudFile = action.CloudFile;
859
                var account = cloudFile.Account ?? accountInfo.UserName;
833
                    var cloudFile = action.CloudFile;
834
                    var account = cloudFile.Account ?? accountInfo.UserName;
860 835

  
861
                var client = new CloudFilesClient(accountInfo);
862
                //Even if GetObjectInfo times out, we can proceed with the upload            
863
                var info = client.GetObjectInfo(account, cloudFile.Container, cloudFile.Name);                
864
                var cloudHash = info.Hash.ToLower();
836
                    var client = new CloudFilesClient(accountInfo);
837
                    //Even if GetObjectInfo times out, we can proceed with the upload            
838
                    var info = client.GetObjectInfo(account, cloudFile.Container, cloudFile.Name);                
839
                    var cloudHash = info.Hash.ToLower();
865 840

  
866
                var hash = action.LocalHash.Value;
867
                var topHash = action.TopHash.Value;
841
                    var hash = action.LocalHash.Value;
842
                    var topHash = action.TopHash.Value;
868 843

  
869
                //If the file hashes match, abort the upload
870
                if (hash == cloudHash  || topHash ==cloudHash)
871
                {
872
                    //but store any metadata changes 
873
                    this.StatusKeeper.StoreInfo(fullFileName, info);
874
                    Log.InfoFormat("Skip upload of {0}, hashes match", fullFileName);
875
                    yield break;
876
                }
844
                    //If the file hashes match, abort the upload
845
                    if (hash == cloudHash  || topHash ==cloudHash)
846
                    {
847
                        //but store any metadata changes 
848
                        this.StatusKeeper.StoreInfo(fullFileName, info);
849
                        Log.InfoFormat("Skip upload of {0}, hashes match", fullFileName);
850
                        return;
851
                    }
877 852

  
878
                if (info.AllowedTo=="read")
879
                    yield break;
853
                    if (info.AllowedTo=="read")
854
                        return;
880 855

  
881
                //Mark the file as modified while we upload it
882
                StatusKeeper.SetFileOverlayStatus(fullFileName, FileOverlayStatus.Modified);
883
                //And then upload it
856
                    //Mark the file as modified while we upload it
857
                    StatusKeeper.SetFileOverlayStatus(fullFileName, FileOverlayStatus.Modified);
858
                    //And then upload it
884 859

  
885
                //Upload even small files using the Hashmap. The server may already containt
886
                //the relevant folder
860
                    //Upload even small files using the Hashmap. The server may already containt
861
                    //the relevant folder
887 862

  
888
                //First, calculate the tree hash
889
                var treeHash = Signature.CalculateTreeHashAsync(fileInfo.FullName, accountInfo.BlockSize,
890
                    accountInfo.BlockHash);
891
                yield return treeHash;
863
                    //First, calculate the tree hash
864
                    var treeHash = await Signature.CalculateTreeHashAsync(fileInfo.FullName, accountInfo.BlockSize,
865
                        accountInfo.BlockHash);                
892 866
                    
893
                yield return Task.Factory.Iterate(UploadWithHashMap(accountInfo,cloudFile,fileInfo,cloudFile.Name,treeHash));
867
                    UploadWithHashMap(accountInfo,cloudFile,fileInfo,cloudFile.Name,treeHash);
894 868

  
895
                //If everything succeeds, change the file and overlay status to normal
896
                this.StatusKeeper.SetFileState(fullFileName, FileStatus.Unchanged, FileOverlayStatus.Normal);
869
                    //If everything succeeds, change the file and overlay status to normal
870
                    this.StatusKeeper.SetFileState(fullFileName, FileStatus.Unchanged, FileOverlayStatus.Normal);
871
                }
872
                //Notify the Shell to update the overlays
873
                NativeMethods.RaiseChangeNotification(fullFileName);
874
                StatusNotification.NotifyChangedFile(fullFileName);
897 875
            }
898
            //Notify the Shell to update the overlays
899
            NativeMethods.RaiseChangeNotification(fullFileName);
900
            StatusNotification.NotifyChangedFile(fullFileName);
876
            catch (AggregateException ex)
877
            {
878
                var exc = ex.InnerException as WebException;
879
                if (exc == null)
880
                    throw ex.InnerException;
881
                var response = exc.Response as HttpWebResponse;
882
                if (response == null)
883
                    throw exc;
884
                if (response.StatusCode == HttpStatusCode.Unauthorized)
885
                {
886
                    Log.Error("Not allowed to upload file", exc);
887
                    var message = String.Format("Not allowed to uplad file {0}", action.LocalFile.FullName);
888
                    StatusKeeper.SetFileState(action.LocalFile.FullName, FileStatus.Unchanged, FileOverlayStatus.Normal);
889
                    StatusNotification.NotifyChange(message, TraceLevel.Warning);
890
                    return;
891
                }
892
                throw;
893
            }
894

  
901 895
        }
902 896

  
903
        public IEnumerable<Task> UploadWithHashMap(AccountInfo accountInfo,ObjectInfo cloudFile,FileInfo fileInfo,string url,Task<TreeHash> treeHash)
897
        public async void UploadWithHashMap(AccountInfo accountInfo,ObjectInfo cloudFile,FileInfo fileInfo,string url,TreeHash treeHash)
904 898
        {
905 899
            if (accountInfo == null)
906 900
                throw new ArgumentNullException("accountInfo");
......
923 917

  
924 918
            var client = new CloudFilesClient(accountInfo);
925 919
            //Send the hashmap to the server            
926
            var hashPut = client.PutHashMap(account, container, url, treeHash.Result);
927
            yield return hashPut;
928

  
929
            var missingHashes = hashPut.Result;
920
            var missingHashes =  await client.PutHashMap(account, container, url, treeHash);
930 921
            //If the server returns no missing hashes, we are done
931 922
            while (missingHashes.Count > 0)
932 923
            {
......
935 926
                foreach (var missingHash in missingHashes)
936 927
                {
937 928
                    //Find the proper block
938
                    var blockIndex = treeHash.Result.HashDictionary[missingHash];
929
                    var blockIndex = treeHash.HashDictionary[missingHash];
939 930
                    var offset = blockIndex*accountInfo.BlockSize;
940 931

  
941 932
                    var read = fileInfo.Read(buffer, offset, accountInfo.BlockSize);
942 933

  
943
                    //And upload the block                
944
                    var postBlock = client.PostBlock(account, container, buffer, 0, read);
934
                    try
935
                    {
936
                        //And upload the block                
937
                        await client.PostBlock(account, container, buffer, 0, read);
938
                        Log.InfoFormat("[BLOCK] Block {0} of {1} uploaded", blockIndex, fullFileName);
939
                    }
940
                    catch (Exception exc)
941
                    {
942
                        Log.ErrorFormat("[ERROR] uploading block {0} of {1}\n{2}", blockIndex, fullFileName, exc);
943
                    }
945 944

  
946
                    //We have to handle possible exceptions in a continuation because
947
                    //*yield return* can't appear inside a try block
948
                    yield return postBlock.ContinueWith(t => 
949
                        t.ReportExceptions(
950
                            exc => Log.ErrorFormat("[ERROR] uploading block {0} of {1}\n{2}", blockIndex, fullFileName, exc),
951
                            ()=>Log.InfoFormat("[BLOCK] Block {0} of {1} uploaded", blockIndex,fullFileName)));
952 945
                }
953 946

  
954
                //Repeat until there are no more missing hashes
955
                hashPut = client.PutHashMap(account, container, url, treeHash.Result);
956
                yield return hashPut;
957
                missingHashes = hashPut.Result;
947
                //Repeat until there are no more missing hashes                
948
                missingHashes = await client.PutHashMap(account, container, url, treeHash);
958 949
            }
959 950
        }
960 951

  
b/trunk/Pithos.Core/Pithos.Core.csproj
134 134
    <CodeAnalysisRuleDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules</CodeAnalysisRuleDirectories>
135 135
  </PropertyGroup>
136 136
  <ItemGroup>
137
    <Reference Include="AsyncCtpLibrary">
138
      <HintPath>C:\Users\Administrator\Documents\Microsoft Visual Studio Async CTP\Samples\AsyncCtpLibrary.dll</HintPath>
139
    </Reference>
137 140
    <Reference Include="Caliburn.Micro, Version=1.2.0.0, Culture=neutral, PublicKeyToken=8e5891231f2ed21f, processorArchitecture=MSIL">
138 141
      <HintPath>..\Libraries\Caliburn.Micro.dll</HintPath>
139 142
    </Reference>
b/trunk/Pithos.Core/PithosMonitor.cs
106 106
                throw new InvalidOperationException("The Authentication url is empty");
107 107
            Contract.EndContractBlock();
108 108

  
109
            //If the account doesn't have a valid path, don't start monitoring but don't throw either
110
            if (String.IsNullOrWhiteSpace(RootPath))
111
                //TODO; Warn user?
112
                return;
113

  
109 114
            StatusNotification.NotifyChange("Starting");
110 115
            if (_started)
111 116
            {
b/trunk/Pithos.Network.Test/Pithos.Network.Test.csproj
75 75
    <CodeAnalysisRuleDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules</CodeAnalysisRuleDirectories>
76 76
  </PropertyGroup>
77 77
  <ItemGroup>
78
    <Reference Include="AsyncCtpLibrary">
79
      <HintPath>C:\Users\Administrator\Documents\Microsoft Visual Studio Async CTP\Samples\AsyncCtpLibrary.dll</HintPath>
80
    </Reference>
78 81
    <Reference Include="Newtonsoft.Json, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b9a188c8922137c6, processorArchitecture=MSIL">
79 82
      <SpecificVersion>False</SpecificVersion>
80 83
      <HintPath>..\Pithos.Network\bin\Debug\Newtonsoft.Json.dll</HintPath>
b/trunk/Pithos.Network/Pithos.Network.csproj
133 133
    <CodeAnalysisRuleDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules</CodeAnalysisRuleDirectories>
134 134
  </PropertyGroup>
135 135
  <ItemGroup>
136
    <Reference Include="AsyncCtpLibrary">
137
      <HintPath>C:\Users\Administrator\Documents\Microsoft Visual Studio Async CTP\Samples\AsyncCtpLibrary.dll</HintPath>
138
    </Reference>
136 139
    <Reference Include="log4net">
137 140
      <HintPath>..\Libraries\log4net.dll</HintPath>
138 141
    </Reference>
b/trunk/Pithos.Network/Signature.cs
108 108
            return hash.Result;
109 109
        }
110 110
        
111
        public static Task<TreeHash> CalculateTreeHashAsync(FileInfo fileInfo, int blockSize, string algorithm)
111
        public static async Task<TreeHash> CalculateTreeHashAsync(FileInfo fileInfo, int blockSize, string algorithm)
112 112
        {
113 113
            if (fileInfo == null)
114 114
                throw new ArgumentNullException("fileInfo");
......
120 120
                throw new ArgumentNullException("algorithm");
121 121
            Contract.EndContractBlock();
122 122

  
123
            return CalculateTreeHashAsync(fileInfo.FullName, blockSize, algorithm);
123
            return await CalculateTreeHashAsync(fileInfo.FullName, blockSize, algorithm);
124 124
        }
125 125

  
126 126

  
127
        public static Task<TreeHash> CalculateTreeHashAsync(string filePath, int blockSize,string algorithm)
127
        public static async Task<TreeHash> CalculateTreeHashAsync(string filePath, int blockSize,string algorithm)
128 128
        {
129 129
            if (String.IsNullOrWhiteSpace(filePath))
130 130
                throw new ArgumentNullException("filePath");
......
136 136

  
137 137
            //DON'T calculate hashes for folders
138 138
            if (Directory.Exists(filePath))
139
                return Task.Factory.StartNew(()=>new TreeHash(algorithm));
139
                return new TreeHash(algorithm);
140 140
            //The hash of a non-existent file is the empty hash
141 141
            if (!File.Exists(filePath))
142
                return Task.Factory.StartNew(()=>new TreeHash(algorithm));
142
                return new TreeHash(algorithm);
143 143

  
144 144
            //Calculate the hash of all blocks using a blockhash iterator
145
            var treeHash =Iterate<TreeHash>(BlockHashIterator(filePath, blockSize, algorithm));
146
            
147
            return treeHash;
148
        }
149

  
150
        
151
        private static IEnumerable<Task> BlockHashIterator(string filePath, int blockSize, string algorithm)
152
        {
153 145
            using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, blockSize, true))
154 146
            {
155 147
                //Calculate the blocks asyncrhonously
156
                var hashes= CalculateBlockHashesAsync(stream, blockSize, algorithm);
157
                yield return hashes;
158
                
148
                var hashes = await CalculateBlockHashesAsync(stream, blockSize, algorithm);                
149

  
159 150
                //And then proceed with creating and returning a TreeHash
160
                var length = stream.Length;                
161
                var list = hashes.Result.OrderBy(pair => pair.Key).Select(pair => pair.Value).ToList();
151
                var length = stream.Length;
152
                var list = hashes.OrderBy(pair => pair.Key).Select(pair => pair.Value).ToList();
162 153

  
163 154
                var treeHash = new TreeHash(algorithm)
164 155
                {
165
                    Bytes = length, 
166
                    BlockSize = blockSize, 
156
                    Bytes = length,
157
                    BlockSize = blockSize,
167 158
                    Hashes = list
168 159
                };
169 160

  
170
                yield return Task.Factory.FromResult(treeHash);
161
                return treeHash;
171 162
            }
172

  
173 163
        }
174 164

  
165
        
175 166
        //The Task Iterator style allows the execution of a sequence of patterns using
176 167
        //iterator syntax.
177 168
        //This particular variation returns the result of the last task, if there is one
b/trunk/Pithos.ShellExtensions/Overlays/IconOverlayBase.cs
56 56

  
57 57
            Debug.WriteLine(String.Format("ICON Status check for {0} - {1}", path, GetType().Name), LogCategories.ShellOverlays);
58 58
            
59
            if (!Settings.Accounts.Any(account=>path.StartsWith(account.RootPath,StringComparison.InvariantCultureIgnoreCase)))
59
            if (!Settings.Accounts.Any(account=>account.RootPath!=null && path.StartsWith(account.RootPath,StringComparison.InvariantCultureIgnoreCase)))
60 60
                return WinError.S_FALSE;
61 61

  
62 62
            var status=StatusChecker.GetFileOverlayStatus(path);

Also available in: Unified diff