-using System;
-using System.Collections.Generic;
-using System.ComponentModel.Composition;
-using System.Diagnostics;
-using System.Diagnostics.Contracts;
-using System.IO;
-using System.Linq;
-using System.Net;
-using System.Reflection;
-using System.Threading;
-using System.Threading.Tasks;
-using Pithos.Interfaces;
-using Pithos.Network;
-using log4net;
-
-namespace Pithos.Core.Agents
-{
- [Export(typeof(Uploader))]
- public class Uploader
- {
- private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
-
- [Import]
- private IStatusKeeper StatusKeeper { get; set; }
-
-
- public IStatusNotification StatusNotification { get; set; }
-
-
- //CancellationTokenSource _cts = new CancellationTokenSource();
- /*public void SignalStop()
- {
- _cts.Cancel();
- }*/
-
- public async Task UploadCloudFile(CloudAction action,CancellationToken cancellationToken)
- {
- if (action == null)
- throw new ArgumentNullException("action");
- Contract.EndContractBlock();
-
- using (ThreadContext.Stacks["Operation"].Push("UploadCloudFile"))
- {
- try
- {
- await UnpauseEvent.WaitAsync();
-
- var fileInfo = action.LocalFile;
-
- if (fileInfo.Extension.Equals("ignore", StringComparison.InvariantCultureIgnoreCase))
- return;
-
- if (Selectives.IsSelected(action.AccountInfo, fileInfo))
- return;
-
- //Try to load the action's local state, if it is empty
- if (action.FileState == null)
- action.FileState = StatusKeeper.GetStateByFilePath(fileInfo.FullName);
- if (action.FileState == null)
- {
- Log.WarnFormat("File [{0}] has no local state. It was probably created by a download action", fileInfo.FullName);
- return;
- }
-
- var latestState = action.FileState;
-
- //Do not upload files in conflict
- if (latestState.FileStatus == FileStatus.Conflict)
- {
- Log.InfoFormat("Skipping file in conflict [{0}]", fileInfo.FullName);
- return;
- }
- //Do not upload files when we have no permission
- if (latestState.FileStatus == FileStatus.Forbidden)
- {
- Log.InfoFormat("Skipping forbidden file [{0}]", fileInfo.FullName);
- return;
- }
-
- //Are we targeting our own account or a sharer account?
- var relativePath = fileInfo.AsRelativeTo(action.AccountInfo.AccountPath);
- var accountInfo = relativePath.StartsWith(FolderConstants.OthersFolder)
- ? GetSharerAccount(relativePath, action.AccountInfo)
- : action.AccountInfo;
-
-
-
- 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 cloudFile = action.CloudFile;
- var account = cloudFile.Account ?? accountInfo.UserName;
- try
- {
-
- var client = new CloudFilesClient(accountInfo);
-
- //Even if GetObjectInfo times out, we can proceed with the upload
- var cloudInfo = client.GetObjectInfo(account, cloudFile.Container, cloudFile.Name);
-
- //If this a shared file
- if (!cloudFile.Account.Equals(action.AccountInfo.UserName,StringComparison.InvariantCultureIgnoreCase))
- {
- //If this is a read-only file, do not upload changes
- if (!cloudInfo.IsWritable(action.AccountInfo.UserName))
- {
- MakeFileReadOnly(fullFileName);
- return;
- }
-
- //If the file is new, can we upload it?
- if ( !cloudInfo.Exists && !client.CanUpload(account, cloudFile))
- {
- MakeFileReadOnly(fullFileName);
- return;
- }
-
- }
-
- await UnpauseEvent.WaitAsync();
-
- if (fileInfo is DirectoryInfo)
- {
- //If the directory doesn't exist the Hash property will be empty
- if (String.IsNullOrWhiteSpace(cloudInfo.Hash))
- //Go on and create the directory
- await client.PutObject(account, cloudFile.Container, cloudFile.Name, fullFileName,
- String.Empty, "application/directory");
- }
- else
- {
-
- var cloudHash = cloudInfo.Hash.ToLower();
-
- string topHash;
- TreeHash treeHash;
- using(StatusNotification.GetNotifier("Hashing {0} for Upload", "Finished hashing {0}",fileInfo.Name))
- {
- treeHash = action.TreeHash.Value;
- topHash = treeHash.TopHash.ToHashString();
- }
-
- //If the file hashes match, abort the upload
- if (cloudInfo != ObjectInfo.Empty && topHash == cloudHash)
- {
- //but store any metadata changes
- StatusKeeper.StoreInfo(fullFileName, cloudInfo);
- 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
-
- //Upload even small files using the Hashmap. The server may already contain
- //the relevant block
-
-
-
- await UploadWithHashMap(accountInfo, cloudFile, fileInfo as FileInfo, cloudFile.Name, treeHash,cancellationToken);
- }
- //If everything succeeds, change the file and overlay status to normal
- StatusKeeper.SetFileState(fullFileName, FileStatus.Unchanged, FileOverlayStatus.Normal, "");
- }
- catch (WebException exc)
- {
- var response = (exc.Response as HttpWebResponse);
- if (response == null)
- throw;
- if (response.StatusCode == HttpStatusCode.Forbidden)
- {
- StatusKeeper.SetFileState(fileInfo.FullName, FileStatus.Forbidden, FileOverlayStatus.Conflict, "Forbidden");
- MakeFileReadOnly(fullFileName);
- }
- else
- //In any other case, propagate the error
- 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;
- }
- }
- }
-
- private static void MakeFileReadOnly(string fullFileName)
- {
- var attributes = File.GetAttributes(fullFileName);
- //Do not make any modifications if not necessary
- if (attributes.HasFlag(FileAttributes.ReadOnly))
- return;
- File.SetAttributes(fullFileName, attributes | FileAttributes.ReadOnly);
- }
-
- private static AccountInfo GetSharerAccount(string relativePath, AccountInfo accountInfo)
- {
- 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
- };
- return accountInfo;
- }
-
-
- public async Task UploadWithHashMap(AccountInfo accountInfo, ObjectInfo cloudFile, FileInfo fileInfo, string url, TreeHash treeHash, CancellationToken token)
- {
- if (accountInfo == null)
- throw new ArgumentNullException("accountInfo");
- if (cloudFile == null)
- throw new ArgumentNullException("cloudFile");
- if (fileInfo == null)
- throw new ArgumentNullException("fileInfo");
- if (String.IsNullOrWhiteSpace(url))
- throw new ArgumentNullException(url);
- if (treeHash == null)
- throw new ArgumentNullException("treeHash");
- if (String.IsNullOrWhiteSpace(cloudFile.Container))
- throw new ArgumentException("Invalid container", "cloudFile");
- Contract.EndContractBlock();
-
-
- using (StatusNotification.GetNotifier("Uploading {0}", "Finished Uploading {0}", fileInfo.Name))
- {
- token.ThrowIfCancellationRequested();
- await UnpauseEvent.WaitAsync();
-
- var fullFileName = fileInfo.GetProperCapitalization();
-
- var account = cloudFile.Account ?? accountInfo.UserName;
- var container = cloudFile.Container;
-
-
- 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)
- {
-
- token.ThrowIfCancellationRequested();
- await UnpauseEvent.WaitAsync();
-
- var buffer = new byte[accountInfo.BlockSize];
- foreach (var missingHash in missingHashes)
- {
- token.ThrowIfCancellationRequested();
- await UnpauseEvent.WaitAsync();
-
- //Find the proper block
- var blockIndex = treeHash.HashDictionary[missingHash];
- long offset = blockIndex*accountInfo.BlockSize;
-
- var read = fileInfo.Read(buffer, offset, accountInfo.BlockSize);
-
- try
- {
- //And upload the block
- await client.PostBlock(account, container, buffer, 0, read);
- Log.InfoFormat("[BLOCK] Block {0} of {1} uploaded", blockIndex, fullFileName);
- }
- catch (Exception exc)
- {
- Log.Error(String.Format("Uploading block {0} of {1}", blockIndex, fullFileName), exc);
- }
- ReportUploadProgress(fileInfo.Name, block++, missingHashes.Count, fileInfo.Length);
- }
-
- token.ThrowIfCancellationRequested();
- //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(totalBlocks == 0
- ? new ProgressNotification(fileName, "Uploading", 1, 1, fileSize)
- : new ProgressNotification(fileName, "Uploading", block, totalBlocks, fileSize));
- }
-
-
- private bool HandleUploadWebException(CloudAction action, WebException exc)
- {
- var response = exc.Response as HttpWebResponse;
- if (response == null)
- throw exc;
- if (response.StatusCode == HttpStatusCode.Unauthorized)
- {
- Log.Error("Not allowed to upload file", exc);
- var message = String.Format("Not allowed to uplad file {0}", action.LocalFile.FullName);
- StatusKeeper.SetFileState(action.LocalFile.FullName, FileStatus.Unchanged, FileOverlayStatus.Normal, "");
- StatusNotification.NotifyChange(message, TraceLevel.Warning);
- return true;
- }
- return false;
- }
-
- [Import]
- public Selectives Selectives { get; set; }
-
- public AsyncManualResetEvent UnpauseEvent { get; set; }
- }
-}
+using System;\r
+using System.Collections.Generic;\r
+using System.ComponentModel.Composition;\r
+using System.Diagnostics;\r
+using System.Diagnostics.Contracts;\r
+using System.IO;\r
+using System.Linq;\r
+using System.Net;\r
+using System.Reflection;\r
+using System.Security.Cryptography;\r
+using System.Threading;\r
+using System.Threading.Tasks;\r
+using Pithos.Interfaces;\r
+using Pithos.Network;\r
+using log4net;\r
+\r
+namespace Pithos.Core.Agents\r
+{\r
+ [Export(typeof(Uploader))]\r
+ public class Uploader\r
+ {\r
+ private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);\r
+\r
+ [Import]\r
+ private IStatusKeeper StatusKeeper { get; set; }\r
+\r
+ \r
+ public IStatusNotification StatusNotification { get; set; }\r
+\r
+ \r
+ //CancellationTokenSource _cts = new CancellationTokenSource();\r
+ /*public void SignalStop()\r
+ {\r
+ _cts.Cancel();\r
+ }*/\r
+\r
+ public async Task UploadCloudFile(CloudAction action,CancellationToken cancellationToken)\r
+ {\r
+ if (action == null)\r
+ throw new ArgumentNullException("action");\r
+ Contract.EndContractBlock();\r
+\r
+ using (ThreadContext.Stacks["Operation"].Push("UploadCloudFile"))\r
+ {\r
+ try\r
+ {\r
+ await UnpauseEvent.WaitAsync();\r
+\r
+ var fileInfo = action.LocalFile;\r
+\r
+ if (fileInfo.Extension.Equals("ignore", StringComparison.InvariantCultureIgnoreCase))\r
+ return;\r
+\r
+ if (!Selectives.IsSelected(action.AccountInfo, fileInfo))\r
+ return;\r
+\r
+ //Try to load the action's local state, if it is empty\r
+ if (action.FileState == null)\r
+ action.FileState = StatusKeeper.GetStateByFilePath(fileInfo.FullName);\r
+ if (action.FileState == null)\r
+ {\r
+ Log.WarnFormat("File [{0}] has no local state. It was probably created by a download action", fileInfo.FullName);\r
+ return;\r
+ }\r
+\r
+ var latestState = action.FileState;\r
+\r
+ //Do not upload files in conflict\r
+ if (latestState.FileStatus == FileStatus.Conflict)\r
+ {\r
+ Log.InfoFormat("Skipping file in conflict [{0}]", fileInfo.FullName);\r
+ return;\r
+ }\r
+ //Do not upload files when we have no permission\r
+ if (latestState.FileStatus == FileStatus.Forbidden)\r
+ {\r
+ Log.InfoFormat("Skipping forbidden file [{0}]", fileInfo.FullName);\r
+ return;\r
+ }\r
+\r
+ //Are we targeting our own account or a sharer account?\r
+ var relativePath = fileInfo.AsRelativeTo(action.AccountInfo.AccountPath);\r
+ var accountInfo = relativePath.StartsWith(FolderConstants.OthersFolder) \r
+ ? GetSharerAccount(relativePath, action.AccountInfo) \r
+ : action.AccountInfo;\r
+\r
+\r
+\r
+ var fullFileName = fileInfo.GetProperCapitalization();\r
+ using (var gate = NetworkGate.Acquire(fullFileName, NetworkOperation.Uploading))\r
+ {\r
+ //Abort if the file is already being uploaded or downloaded\r
+ if (gate.Failed)\r
+ return;\r
+\r
+ var cloudFile = action.CloudFile;\r
+ var account = cloudFile.Account ?? accountInfo.UserName;\r
+ try\r
+ {\r
+\r
+ var client = new CloudFilesClient(accountInfo);\r
+\r
+ //Even if GetObjectInfo times out, we can proceed with the upload \r
+ var cloudInfo = client.GetObjectInfo(account, cloudFile.Container, cloudFile.Name);\r
+\r
+ //If this a shared file\r
+ if (!cloudFile.Account.Equals(action.AccountInfo.UserName,StringComparison.InvariantCultureIgnoreCase))\r
+ {\r
+ \r
+/*\r
+ if (!cloudInfo.IsWritable(action.AccountInfo.UserName))\r
+ {\r
+ MakeFileReadOnly(fullFileName);\r
+ StatusKeeper.SetFileState(fullFileName, FileStatus.Unchanged, FileOverlayStatus.Normal, "");\r
+ return;\r
+ }\r
+*/\r
+\r
+ //If this is a read-only file, do not upload changes\r
+ if ( !cloudInfo.IsWritable(action.AccountInfo.UserName) ||\r
+ //If the file is new, but we can't upload it\r
+ (!cloudInfo.Exists && !client.CanUpload(account, cloudFile)) )\r
+ {\r
+ MakeFileReadOnly(fullFileName);\r
+ StatusKeeper.SetFileState(fullFileName, FileStatus.Unchanged, FileOverlayStatus.Normal, "");\r
+ return;\r
+ }\r
+\r
+ }\r
+\r
+ await UnpauseEvent.WaitAsync();\r
+\r
+ if (fileInfo is DirectoryInfo)\r
+ {\r
+ //If the directory doesn't exist the Hash property will be empty\r
+ if (String.IsNullOrWhiteSpace(cloudInfo.Hash))\r
+ //Go on and create the directory\r
+ await client.PutObject(account, cloudFile.Container, cloudFile.Name, fullFileName,\r
+ String.Empty, "application/directory");\r
+ }\r
+ else\r
+ {\r
+\r
+ var cloudHash = cloudInfo.Hash.ToLower();\r
+\r
+ string topHash;\r
+ TreeHash treeHash;\r
+ using(StatusNotification.GetNotifier("Hashing {0} for Upload", "Finished hashing {0}",fileInfo.Name))\r
+ {\r
+ treeHash = action.TreeHash.Value;\r
+ topHash = treeHash.TopHash.ToHashString();\r
+ }\r
+\r
+\r
+\r
+ //If the file hashes match, abort the upload\r
+ if (cloudInfo != ObjectInfo.Empty && (topHash == cloudHash ))\r
+ {\r
+ //but store any metadata changes \r
+ StatusKeeper.StoreInfo(fullFileName, cloudInfo);\r
+ Log.InfoFormat("Skip upload of {0}, hashes match", fullFileName);\r
+ return;\r
+ }\r
+\r
+\r
+ //Mark the file as modified while we upload it\r
+ StatusKeeper.SetFileOverlayStatus(fullFileName, FileOverlayStatus.Modified);\r
+ //And then upload it\r
+\r
+ //Upload even small files using the Hashmap. The server may already contain\r
+ //the relevant block \r
+\r
+ \r
+\r
+ await UploadWithHashMap(accountInfo, cloudFile, fileInfo as FileInfo, cloudFile.Name, treeHash,cancellationToken);\r
+ }\r
+ //If everything succeeds, change the file and overlay status to normal\r
+ StatusKeeper.SetFileState(fullFileName, FileStatus.Unchanged, FileOverlayStatus.Normal, "");\r
+ }\r
+ catch (WebException exc)\r
+ {\r
+ var response = (exc.Response as HttpWebResponse);\r
+ if (response == null)\r
+ throw;\r
+ if (response.StatusCode == HttpStatusCode.Forbidden)\r
+ {\r
+ StatusKeeper.SetFileState(fileInfo.FullName, FileStatus.Forbidden, FileOverlayStatus.Conflict, "Forbidden");\r
+ MakeFileReadOnly(fullFileName);\r
+ }\r
+ else\r
+ //In any other case, propagate the error\r
+ throw;\r
+ }\r
+ }\r
+ //Notify the Shell to update the overlays\r
+ NativeMethods.RaiseChangeNotification(fullFileName);\r
+ StatusNotification.NotifyChangedFile(fullFileName);\r
+ }\r
+ catch (AggregateException ex)\r
+ {\r
+ var exc = ex.InnerException as WebException;\r
+ if (exc == null)\r
+ throw ex.InnerException;\r
+ if (HandleUploadWebException(action, exc))\r
+ return;\r
+ throw;\r
+ }\r
+ catch (WebException ex)\r
+ {\r
+ if (HandleUploadWebException(action, ex))\r
+ return;\r
+ throw;\r
+ }\r
+ catch (Exception ex)\r
+ {\r
+ Log.Error("Unexpected error while uploading file", ex);\r
+ throw;\r
+ }\r
+ }\r
+ }\r
+\r
+ private static void MakeFileReadOnly(string fullFileName)\r
+ {\r
+ var attributes = File.GetAttributes(fullFileName);\r
+ //Do not make any modifications if not necessary\r
+ if (attributes.HasFlag(FileAttributes.ReadOnly))\r
+ return;\r
+ File.SetAttributes(fullFileName, attributes | FileAttributes.ReadOnly); \r
+ }\r
+\r
+ private static AccountInfo GetSharerAccount(string relativePath, AccountInfo accountInfo)\r
+ {\r
+ var parts = relativePath.Split('\\');\r
+ var accountName = parts[1];\r
+ var oldName = accountInfo.UserName;\r
+ var absoluteUri = accountInfo.StorageUri.AbsoluteUri;\r
+ var nameIndex = absoluteUri.IndexOf(oldName, StringComparison.Ordinal);\r
+ var root = absoluteUri.Substring(0, nameIndex);\r
+\r
+ accountInfo = new AccountInfo\r
+ {\r
+ UserName = accountName,\r
+ AccountPath = Path.Combine(accountInfo.AccountPath, parts[0], parts[1]),\r
+ StorageUri = new Uri(root + accountName),\r
+ BlockHash = accountInfo.BlockHash,\r
+ BlockSize = accountInfo.BlockSize,\r
+ Token = accountInfo.Token\r
+ };\r
+ return accountInfo;\r
+ }\r
+\r
+\r
+ public async Task UploadWithHashMap(AccountInfo accountInfo, ObjectInfo cloudFile, FileInfo fileInfo, string url, TreeHash treeHash, CancellationToken token)\r
+ {\r
+ if (accountInfo == null)\r
+ throw new ArgumentNullException("accountInfo");\r
+ if (cloudFile == null)\r
+ throw new ArgumentNullException("cloudFile");\r
+ if (fileInfo == null)\r
+ throw new ArgumentNullException("fileInfo");\r
+ if (String.IsNullOrWhiteSpace(url))\r
+ throw new ArgumentNullException(url);\r
+ if (treeHash == null)\r
+ throw new ArgumentNullException("treeHash");\r
+ if (String.IsNullOrWhiteSpace(cloudFile.Container))\r
+ throw new ArgumentException("Invalid container", "cloudFile");\r
+ Contract.EndContractBlock();\r
+\r
+ \r
+ using (StatusNotification.GetNotifier("Uploading {0}", "Finished Uploading {0}", fileInfo.Name))\r
+ {\r
+ if (await WaitOrAbort(accountInfo,cloudFile, token)) \r
+ return;\r
+\r
+ var fullFileName = fileInfo.GetProperCapitalization();\r
+\r
+ var account = cloudFile.Account ?? accountInfo.UserName;\r
+ var container = cloudFile.Container;\r
+\r
+\r
+ var client = new CloudFilesClient(accountInfo);\r
+ //Send the hashmap to the server \r
+ var missingHashes = await client.PutHashMap(account, container, url, treeHash);\r
+ int block = 0;\r
+ ReportUploadProgress(fileInfo.Name, block++, missingHashes.Count, fileInfo.Length);\r
+ //If the server returns no missing hashes, we are done\r
+ while (missingHashes.Count > 0)\r
+ {\r
+\r
+ if (await WaitOrAbort(accountInfo,cloudFile, token))\r
+ return;\r
+\r
+\r
+ var buffer = new byte[accountInfo.BlockSize];\r
+ foreach (var missingHash in missingHashes)\r
+ {\r
+ if (await WaitOrAbort(accountInfo,cloudFile, token))\r
+ return;\r
+\r
+\r
+ //Find the proper block\r
+ var blockIndex = treeHash.HashDictionary[missingHash];\r
+ long offset = blockIndex*accountInfo.BlockSize;\r
+\r
+ var read = fileInfo.Read(buffer, offset, accountInfo.BlockSize);\r
+\r
+ try\r
+ {\r
+ //And upload the block \r
+ await client.PostBlock(account, container, buffer, 0, read);\r
+ Log.InfoFormat("[BLOCK] Block {0} of {1} uploaded", blockIndex, fullFileName);\r
+ }\r
+ catch (Exception exc)\r
+ {\r
+ Log.Error(String.Format("Uploading block {0} of {1}", blockIndex, fullFileName), exc);\r
+ }\r
+ ReportUploadProgress(fileInfo.Name, block++, missingHashes.Count, fileInfo.Length);\r
+ }\r
+\r
+ token.ThrowIfCancellationRequested();\r
+ //Repeat until there are no more missing hashes \r
+ missingHashes = await client.PutHashMap(account, container, url, treeHash);\r
+ }\r
+\r
+ ReportUploadProgress(fileInfo.Name, missingHashes.Count, missingHashes.Count, fileInfo.Length);\r
+ }\r
+ }\r
+\r
+ private async Task<bool> WaitOrAbort(AccountInfo account,ObjectInfo cloudFile, CancellationToken token)\r
+ {\r
+ token.ThrowIfCancellationRequested();\r
+ await UnpauseEvent.WaitAsync();\r
+ var shouldAbort = !Selectives.IsSelected(account,cloudFile);\r
+ if (shouldAbort)\r
+ Log.InfoFormat("Aborting [{0}]",cloudFile.Uri);\r
+ return shouldAbort;\r
+ }\r
+\r
+ private void ReportUploadProgress(string fileName, int block, int totalBlocks, long fileSize)\r
+ {\r
+ StatusNotification.Notify(totalBlocks == 0\r
+ ? new ProgressNotification(fileName, "Uploading", 1, 1, fileSize)\r
+ : new ProgressNotification(fileName, "Uploading", block, totalBlocks, fileSize));\r
+ }\r
+\r
+\r
+ private bool HandleUploadWebException(CloudAction action, WebException exc)\r
+ {\r
+ var response = exc.Response as HttpWebResponse;\r
+ if (response == null)\r
+ throw exc;\r
+ if (response.StatusCode == HttpStatusCode.Unauthorized)\r
+ {\r
+ Log.Error("Not allowed to upload file", exc);\r
+ var message = String.Format("Not allowed to uplad file {0}", action.LocalFile.FullName);\r
+ StatusKeeper.SetFileState(action.LocalFile.FullName, FileStatus.Unchanged, FileOverlayStatus.Normal, "");\r
+ StatusNotification.NotifyChange(message, TraceLevel.Warning);\r
+ return true;\r
+ }\r
+ return false;\r
+ }\r
+\r
+ [Import]\r
+ public Selectives Selectives { get; set; }\r
+\r
+ public AsyncManualResetEvent UnpauseEvent { get; set; }\r
+ }\r
+}\r