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