Revision 6bcdd8e2 trunk/Pithos.Core/Agents/NetworkAgent.cs

b/trunk/Pithos.Core/Agents/NetworkAgent.cs
115 115

  
116 116

  
117 117

  
118
            using (log4net.ThreadContext.Stacks["NETWORK"].Push("PROCESS"))
118
            using (log4net.ThreadContext.Stacks["Operation"].Push(action.ToString()))
119 119
            {                
120
                Log.InfoFormat("[ACTION] Start Processing {0}", action);
121 120

  
122 121
                var cloudFile = action.CloudFile;
123 122
                var downloadPath = action.GetDownloadPath();
......
125 124
                try
126 125
                {
127 126
                    _proceedEvent.Reset();
128
                    UpdateStatus(PithosStatus.Syncing);
127
                    //UpdateStatus(PithosStatus.Syncing);
129 128
                    var accountInfo = action.AccountInfo;
130 129

  
131 130
                    if (action.Action == CloudActionType.DeleteCloud)
......
169 168
                                break;
170 169
                        }
171 170
                    }
172
                    Log.InfoFormat("[ACTION] End Processing {0}:{1}->{2}", action.Action, action.LocalFile,
171
                    Log.InfoFormat("End Processing {0}:{1}->{2}", action.Action, action.LocalFile,
173 172
                                           action.CloudFile.Name);
174 173
                }
175 174
                catch (WebException exc)
......
230 229
            if (action.CloudFile == null)
231 230
                throw new ArgumentException("The action's cloud file is not specified", "action");
232 231
            Contract.EndContractBlock();
232
            using (ThreadContext.Stacks["Operation"].Push("RenameLocalFile"))
233
            {
233 234

  
234
            //We assume that the local file already exists, otherwise the poll agent
235
            //would have issued a download request
236

  
237
            var currentInfo = action.CloudFile;
238
            var previousInfo = action.CloudFile.Previous;
239
            var fileAgent = FileAgent.GetFileAgent(accountInfo);
235
                //We assume that the local file already exists, otherwise the poll agent
236
                //would have issued a download request
240 237

  
241
            var previousRelativepath = previousInfo.RelativeUrlToFilePath(accountInfo.UserName);
242
            var previousFile = fileAgent.GetFileSystemInfo(previousRelativepath);
238
                var currentInfo = action.CloudFile;
239
                var previousInfo = action.CloudFile.Previous;
240
                var fileAgent = FileAgent.GetFileAgent(accountInfo);
243 241

  
244
            //In every case we need to move the local file first
245
            MoveLocalFile(accountInfo, previousFile, fileAgent, currentInfo);
242
                var previousRelativepath = previousInfo.RelativeUrlToFilePath(accountInfo.UserName);
243
                var previousFile = fileAgent.GetFileSystemInfo(previousRelativepath);
246 244

  
245
                //In every case we need to move the local file first
246
                MoveLocalFile(accountInfo, previousFile, fileAgent, currentInfo);
247
            }
247 248
        }
248 249

  
249 250
        private void MoveLocalFile(AccountInfo accountInfo, FileSystemInfo previousFile, FileAgent fileAgent,
......
287 288
            if (action.CloudFile== null)
288 289
                throw new ArgumentException("The action's cloud file is not specified", "action");
289 290
            Contract.EndContractBlock();
291
            using (ThreadContext.Stacks["Operation"].Push("SyncFiles"))
292
            {
290 293

  
291
            var localFile = action.LocalFile;
292
            var cloudFile = action.CloudFile;
293
            var downloadPath=action.LocalFile.GetProperCapitalization();
294
                var localFile = action.LocalFile;
295
                var cloudFile = action.CloudFile;
296
                var downloadPath = action.LocalFile.GetProperCapitalization();
294 297

  
295
            var cloudHash = cloudFile.Hash.ToLower();
296
            var previousCloudHash = cloudFile.PreviousHash.ToLower();
297
            var localHash = action.LocalHash.Value.ToLower();
298
            var topHash = action.TopHash.Value.ToLower();
298
                var cloudHash = cloudFile.Hash.ToLower();
299
                var previousCloudHash = cloudFile.PreviousHash.ToLower();
300
                var localHash = action.LocalHash.Value.ToLower();
301
                var topHash = action.TopHash.Value.ToLower();
299 302

  
300
            //At this point we know that an object has changed on the server and that a local
301
            //file already exists. We need to decide whether the file has only changed on 
302
            //the server or there is a conflicting change on the client.
303
            //
304
            
305
            //Not enough to compare only the local hashes (MD5), also have to compare the tophashes            
306
            //If any of the hashes match, we are done
307
            if ((cloudHash == localHash || cloudHash == topHash))
308
            {
309
                Log.InfoFormat("Skipping {0}, hashes match",downloadPath);
310
                return;
311
            }
303
                //At this point we know that an object has changed on the server and that a local
304
                //file already exists. We need to decide whether the file has only changed on 
305
                //the server or there is a conflicting change on the client.
306
                //
312 307

  
313
            //The hashes DON'T match. We need to sync
308
                //Not enough to compare only the local hashes (MD5), also have to compare the tophashes            
309
                //If any of the hashes match, we are done
310
                if ((cloudHash == localHash || cloudHash == topHash))
311
                {
312
                    Log.InfoFormat("Skipping {0}, hashes match", downloadPath);
313
                    return;
314
                }
314 315

  
315
            // If the previous tophash matches the local tophash, the file was only changed on the server. 
316
            if (localHash == previousCloudHash)
317
            {
318
                await DownloadCloudFile(accountInfo, cloudFile, downloadPath);
319
            }
320
            else
321
            {
322
                //If the previous and local hash don't match, there was a local conflict
323
                //that was not uploaded to the server. We have a conflict
324
                ReportConflict(downloadPath);
316
                //The hashes DON'T match. We need to sync
317

  
318
                // If the previous tophash matches the local tophash, the file was only changed on the server. 
319
                if (localHash == previousCloudHash)
320
                {
321
                    await DownloadCloudFile(accountInfo, cloudFile, downloadPath);
322
                }
323
                else
324
                {
325
                    //If the previous and local hash don't match, there was a local conflict
326
                    //that was not uploaded to the server. We have a conflict
327
                    ReportConflict(downloadPath);
328
                }
325 329
            }
326 330
        }
327 331

  
......
413 417
            if (action.OldCloudFile==null)
414 418
                throw new ArgumentException("OldCloudFile","action");
415 419
            Contract.EndContractBlock();
416
            
417
            
418
            var newFilePath = action.LocalFile.FullName;
419
            
420
            //How do we handle concurrent renames and deletes/uploads/downloads?
421
            //* A conflicting upload means that a file was renamed before it had a chance to finish uploading
422
            //  This should never happen as the network agent executes only one action at a time
423
            //* A conflicting download means that the file was modified on the cloud. While we can go on and complete
424
            //  the rename, there may be a problem if the file is downloaded in blocks, as subsequent block requests for the 
425
            //  same name will fail.
426
            //  This should never happen as the network agent executes only one action at a time.
427
            //* A conflicting delete can happen if the rename was followed by a delete action that didn't have the chance
428
            //  to remove the rename from the queue.
429
            //  We can probably ignore this case. It will result in an error which should be ignored            
430 420

  
431
            
432
            //The local file is already renamed
433
            StatusKeeper.SetFileOverlayStatus(newFilePath, FileOverlayStatus.Modified);
421
            using (ThreadContext.Stacks["Operation"].Push("RenameCloudFile"))
422
            {
434 423

  
424
                var newFilePath = action.LocalFile.FullName;
425

  
426
                //How do we handle concurrent renames and deletes/uploads/downloads?
427
                //* A conflicting upload means that a file was renamed before it had a chance to finish uploading
428
                //  This should never happen as the network agent executes only one action at a time
429
                //* A conflicting download means that the file was modified on the cloud. While we can go on and complete
430
                //  the rename, there may be a problem if the file is downloaded in blocks, as subsequent block requests for the 
431
                //  same name will fail.
432
                //  This should never happen as the network agent executes only one action at a time.
433
                //* A conflicting delete can happen if the rename was followed by a delete action that didn't have the chance
434
                //  to remove the rename from the queue.
435
                //  We can probably ignore this case. It will result in an error which should be ignored            
435 436

  
436
            var account = action.CloudFile.Account ?? accountInfo.UserName;
437
            var container = action.CloudFile.Container;
438
            
439
            var client = new CloudFilesClient(accountInfo);
440
            //TODO: What code is returned when the source file doesn't exist?
441
            client.MoveObject(account, container, action.OldCloudFile.Name, container, action.CloudFile.Name);
442 437

  
443
            StatusKeeper.SetFileStatus(newFilePath, FileStatus.Unchanged);
444
            StatusKeeper.SetFileOverlayStatus(newFilePath, FileOverlayStatus.Normal);
445
            NativeMethods.RaiseChangeNotification(newFilePath);
438
                //The local file is already renamed
439
                StatusKeeper.SetFileOverlayStatus(newFilePath, FileOverlayStatus.Modified);
440

  
441

  
442
                var account = action.CloudFile.Account ?? accountInfo.UserName;
443
                var container = action.CloudFile.Container;
444

  
445
                var client = new CloudFilesClient(accountInfo);
446
                //TODO: What code is returned when the source file doesn't exist?
447
                client.MoveObject(account, container, action.OldCloudFile.Name, container, action.CloudFile.Name);
448

  
449
                StatusKeeper.SetFileStatus(newFilePath, FileStatus.Unchanged);
450
                StatusKeeper.SetFileOverlayStatus(newFilePath, FileOverlayStatus.Normal);
451
                NativeMethods.RaiseChangeNotification(newFilePath);
452
            }
446 453
        }
447 454

  
448 455
        //Download a file.
......
461 468
            if (!Path.IsPathRooted(filePath))
462 469
                throw new ArgumentException("The filePath must be rooted", "filePath");
463 470
            Contract.EndContractBlock();
464
            
465 471

  
466
            var localPath = Interfaces.FileInfoExtensions.GetProperFilePathCapitalization(filePath);
467
            var relativeUrl = new Uri(cloudFile.Name, UriKind.Relative);
468

  
469
            var url = relativeUrl.ToString();
470
            if (cloudFile.Name.EndsWith(".ignore", StringComparison.InvariantCultureIgnoreCase))
471
                return;
472
            using (ThreadContext.Stacks["Operation"].Push("DownloadCloudFile"))
473
            {
472 474

  
475
                var localPath = Interfaces.FileInfoExtensions.GetProperFilePathCapitalization(filePath);
476
                var relativeUrl = new Uri(cloudFile.Name, UriKind.Relative);
473 477

  
474
            //Are we already downloading or uploading the file? 
475
            using (var gate=NetworkGate.Acquire(localPath, NetworkOperation.Downloading))
476
            {
477
                if (gate.Failed)
478
                var url = relativeUrl.ToString();
479
                if (cloudFile.Name.EndsWith(".ignore", StringComparison.InvariantCultureIgnoreCase))
478 480
                    return;
479
                
480
                var client = new CloudFilesClient(accountInfo);
481
                var account = cloudFile.Account;
482
                var container = cloudFile.Container;
483 481

  
484
                if (cloudFile.Content_Type == @"application/directory")
482

  
483
                //Are we already downloading or uploading the file? 
484
                using (var gate = NetworkGate.Acquire(localPath, NetworkOperation.Downloading))
485 485
                {
486
                    if (!Directory.Exists(localPath))
487
                        Directory.CreateDirectory(localPath);
488
                }
489
                else
490
                {                    
491
                    //Retrieve the hashmap from the server
492
                    var serverHash = await client.GetHashMap(account, container, url);
493
                    //If it's a small file
494
                    if (serverHash.Hashes.Count == 1)
495
                        //Download it in one go
496
                        await
497
                            DownloadEntireFileAsync(accountInfo, client, cloudFile, relativeUrl, localPath, serverHash);
498
                        //Otherwise download it block by block
499
                    else
500
                        await DownloadWithBlocks(accountInfo, client, cloudFile, relativeUrl, localPath, serverHash);
486
                    if (gate.Failed)
487
                        return;
488

  
489
                    var client = new CloudFilesClient(accountInfo);
490
                    var account = cloudFile.Account;
491
                    var container = cloudFile.Container;
501 492

  
502
                    if (cloudFile.AllowedTo == "read")
493
                    if (cloudFile.Content_Type == @"application/directory")
503 494
                    {
504
                        var attributes = File.GetAttributes(localPath);
505
                        File.SetAttributes(localPath, attributes | FileAttributes.ReadOnly);                        
495
                        if (!Directory.Exists(localPath))
496
                            Directory.CreateDirectory(localPath);
497
                    }
498
                    else
499
                    {
500
                        //Retrieve the hashmap from the server
501
                        var serverHash = await client.GetHashMap(account, container, url);
502
                        //If it's a small file
503
                        if (serverHash.Hashes.Count == 1)
504
                            //Download it in one go
505
                            await
506
                                DownloadEntireFileAsync(accountInfo, client, cloudFile, relativeUrl, localPath,
507
                                                        serverHash);
508
                            //Otherwise download it block by block
509
                        else
510
                            await DownloadWithBlocks(accountInfo, client, cloudFile, relativeUrl, localPath, serverHash);
511

  
512
                        if (cloudFile.AllowedTo == "read")
513
                        {
514
                            var attributes = File.GetAttributes(localPath);
515
                            File.SetAttributes(localPath, attributes | FileAttributes.ReadOnly);
516
                        }
506 517
                    }
507
                }
508 518

  
509
                //Now we can store the object's metadata without worrying about ghost status entries
510
                StatusKeeper.StoreInfo(localPath, cloudFile);
511
                
519
                    //Now we can store the object's metadata without worrying about ghost status entries
520
                    StatusKeeper.StoreInfo(localPath, cloudFile);
521

  
522
                }
512 523
            }
513 524
        }
514 525

  
......
604 615
            //And compare it with the server's hash
605 616
            var upHashes = serverHash.GetHashesAsStrings();
606 617
            var localHashes = treeHash.HashDictionary;
618
            ReportDownloadProgress(Path.GetFileName(localPath),0,upHashes.Length,cloudFile.Bytes);
607 619
            for (int i = 0; i < upHashes.Length; i++)
608 620
            {
609 621
                //For every non-matching hash
......
633 645

  
634 646
                    Log.InfoFormat("[BLOCK GET] FINISH {0} of {1} for {2}", i, upHashes.Length, localPath);
635 647
                }
648
                ReportDownloadProgress(Path.GetFileName(localPath), i, upHashes.Length, cloudFile.Bytes);
636 649
            }
637 650

  
638 651
            //Want to avoid notifications if no changes were made
......
652 665
            if (action == null)
653 666
                throw new ArgumentNullException("action");           
654 667
            Contract.EndContractBlock();
655

  
656
            try
657
            {                
658
                var accountInfo = action.AccountInfo;
659

  
660
                var fileInfo = action.LocalFile;
661

  
662
                if (fileInfo.Extension.Equals("ignore", StringComparison.InvariantCultureIgnoreCase))
663
                    return;
664
                
665
                var relativePath = fileInfo.AsRelativeTo(accountInfo.AccountPath);
666
                if (relativePath.StartsWith(FolderConstants.OthersFolder))
668
            using(ThreadContext.Stacks["Operation"].Push("UploadCloudFile"))
669
            {
670
                try
667 671
                {
668
                    var parts = relativePath.Split('\\');
669
                    var accountName = parts[1];
670
                    var oldName = accountInfo.UserName;
671
                    var absoluteUri = accountInfo.StorageUri.AbsoluteUri;
672
                    var nameIndex = absoluteUri.IndexOf(oldName, StringComparison.Ordinal);
673
                    var root = absoluteUri.Substring(0, nameIndex);
674

  
675
                    accountInfo = new AccountInfo
676
                    {
677
                        UserName = accountName,
678
                        AccountPath = Path.Combine(accountInfo.AccountPath, parts[0], parts[1]),
679
                        StorageUri = new Uri(root + accountName),
680
                        BlockHash = accountInfo.BlockHash,
681
                        BlockSize = accountInfo.BlockSize,
682
                        Token = accountInfo.Token
683
                    };
684
                }
672
                    var accountInfo = action.AccountInfo;
685 673

  
674
                    var fileInfo = action.LocalFile;
686 675

  
687
                var fullFileName = fileInfo.GetProperCapitalization();
688
                using (var gate = NetworkGate.Acquire(fullFileName, NetworkOperation.Uploading))
689
                {
690
                    //Abort if the file is already being uploaded or downloaded
691
                    if (gate.Failed)
676
                    if (fileInfo.Extension.Equals("ignore", StringComparison.InvariantCultureIgnoreCase))
692 677
                        return;
693 678

  
694
                    var cloudFile = action.CloudFile;
695
                    var account = cloudFile.Account ?? accountInfo.UserName;
696

  
697
                    var client = new CloudFilesClient(accountInfo);                    
698
                    //Even if GetObjectInfo times out, we can proceed with the upload            
699
                    var info = client.GetObjectInfo(account, cloudFile.Container, cloudFile.Name);
700

  
701
                    //If this is a read-only file, do not upload changes
702
                    if (info.AllowedTo == "read")
703
                        return;
704
                    
705
                    //TODO: Check how a directory hash is calculated -> All dirs seem to have the same hash
706
                    if (fileInfo is DirectoryInfo)
679
                    var relativePath = fileInfo.AsRelativeTo(accountInfo.AccountPath);
680
                    if (relativePath.StartsWith(FolderConstants.OthersFolder))
707 681
                    {
708
                        //If the directory doesn't exist the Hash property will be empty
709
                        if (String.IsNullOrWhiteSpace(info.Hash))
710
                            //Go on and create the directory
711
                            await client.PutObject(account, cloudFile.Container, cloudFile.Name, fullFileName, String.Empty, "application/directory");
682
                        var parts = relativePath.Split('\\');
683
                        var accountName = parts[1];
684
                        var oldName = accountInfo.UserName;
685
                        var absoluteUri = accountInfo.StorageUri.AbsoluteUri;
686
                        var nameIndex = absoluteUri.IndexOf(oldName, StringComparison.Ordinal);
687
                        var root = absoluteUri.Substring(0, nameIndex);
688

  
689
                        accountInfo = new AccountInfo
690
                                          {
691
                                              UserName = accountName,
692
                                              AccountPath = Path.Combine(accountInfo.AccountPath, parts[0], parts[1]),
693
                                              StorageUri = new Uri(root + accountName),
694
                                              BlockHash = accountInfo.BlockHash,
695
                                              BlockSize = accountInfo.BlockSize,
696
                                              Token = accountInfo.Token
697
                                          };
712 698
                    }
713
                    else
699

  
700

  
701
                    var fullFileName = fileInfo.GetProperCapitalization();
702
                    using (var gate = NetworkGate.Acquire(fullFileName, NetworkOperation.Uploading))
714 703
                    {
704
                        //Abort if the file is already being uploaded or downloaded
705
                        if (gate.Failed)
706
                            return;
715 707

  
716
                        var cloudHash = info.Hash.ToLower();
708
                        var cloudFile = action.CloudFile;
709
                        var account = cloudFile.Account ?? accountInfo.UserName;
717 710

  
718
                        var hash = action.LocalHash.Value;
719
                        var topHash = action.TopHash.Value;
711
                        var client = new CloudFilesClient(accountInfo);
712
                        //Even if GetObjectInfo times out, we can proceed with the upload            
713
                        var info = client.GetObjectInfo(account, cloudFile.Container, cloudFile.Name);
720 714

  
721
                        //If the file hashes match, abort the upload
722
                        if (hash == cloudHash || topHash == cloudHash)
723
                        {
724
                            //but store any metadata changes 
725
                            StatusKeeper.StoreInfo(fullFileName, info);
726
                            Log.InfoFormat("Skip upload of {0}, hashes match", fullFileName);
715
                        //If this is a read-only file, do not upload changes
716
                        if (info.AllowedTo == "read")
727 717
                            return;
718

  
719
                        //TODO: Check how a directory hash is calculated -> All dirs seem to have the same hash
720
                        if (fileInfo is DirectoryInfo)
721
                        {
722
                            //If the directory doesn't exist the Hash property will be empty
723
                            if (String.IsNullOrWhiteSpace(info.Hash))
724
                                //Go on and create the directory
725
                                await
726
                                    client.PutObject(account, cloudFile.Container, cloudFile.Name, fullFileName,
727
                                                     String.Empty, "application/directory");
728 728
                        }
729
                        else
730
                        {
731

  
732
                            var cloudHash = info.Hash.ToLower();
733

  
734
                            var hash = action.LocalHash.Value;
735
                            var topHash = action.TopHash.Value;
736

  
737
                            //If the file hashes match, abort the upload
738
                            if (hash == cloudHash || topHash == cloudHash)
739
                            {
740
                                //but store any metadata changes 
741
                                StatusKeeper.StoreInfo(fullFileName, info);
742
                                Log.InfoFormat("Skip upload of {0}, hashes match", fullFileName);
743
                                return;
744
                            }
729 745

  
730 746

  
731
                        //Mark the file as modified while we upload it
732
                        StatusKeeper.SetFileOverlayStatus(fullFileName, FileOverlayStatus.Modified);
733
                        //And then upload it
747
                            //Mark the file as modified while we upload it
748
                            StatusKeeper.SetFileOverlayStatus(fullFileName, FileOverlayStatus.Modified);
749
                            //And then upload it
734 750

  
735
                        //Upload even small files using the Hashmap. The server may already contain
736
                        //the relevant block
751
                            //Upload even small files using the Hashmap. The server may already contain
752
                            //the relevant block
737 753

  
738
                        //First, calculate the tree hash
739
                        var treeHash = await Signature.CalculateTreeHashAsync(fullFileName, accountInfo.BlockSize,
740
                                                                              accountInfo.BlockHash, 2);
754
                            //First, calculate the tree hash
755
                            var treeHash = await Signature.CalculateTreeHashAsync(fullFileName, accountInfo.BlockSize,
756
                                                                                  accountInfo.BlockHash, 2);
741 757

  
742
                        await UploadWithHashMap(accountInfo, cloudFile, fileInfo as FileInfo, cloudFile.Name, treeHash);
758
                            await
759
                                UploadWithHashMap(accountInfo, cloudFile, fileInfo as FileInfo, cloudFile.Name, treeHash);
760
                        }
761
                        //If everything succeeds, change the file and overlay status to normal
762
                        StatusKeeper.SetFileState(fullFileName, FileStatus.Unchanged, FileOverlayStatus.Normal);
743 763
                    }
744
                    //If everything succeeds, change the file and overlay status to normal
745
                    StatusKeeper.SetFileState(fullFileName, FileStatus.Unchanged, FileOverlayStatus.Normal);
764
                    //Notify the Shell to update the overlays
765
                    NativeMethods.RaiseChangeNotification(fullFileName);
766
                    StatusNotification.NotifyChangedFile(fullFileName);
767
                }
768
                catch (AggregateException ex)
769
                {
770
                    var exc = ex.InnerException as WebException;
771
                    if (exc == null)
772
                        throw ex.InnerException;
773
                    if (HandleUploadWebException(action, exc))
774
                        return;
775
                    throw;
776
                }
777
                catch (WebException ex)
778
                {
779
                    if (HandleUploadWebException(action, ex))
780
                        return;
781
                    throw;
782
                }
783
                catch (Exception ex)
784
                {
785
                    Log.Error("Unexpected error while uploading file", ex);
786
                    throw;
746 787
                }
747
                //Notify the Shell to update the overlays
748
                NativeMethods.RaiseChangeNotification(fullFileName);
749
                StatusNotification.NotifyChangedFile(fullFileName);
750
            }
751
            catch (AggregateException ex)
752
            {
753
                var exc = ex.InnerException as WebException;
754
                if (exc == null)
755
                    throw ex.InnerException;
756
                if (HandleUploadWebException(action, exc)) 
757
                    return;
758
                throw;
759
            }
760
            catch (WebException ex)
761
            {
762
                if (HandleUploadWebException(action, ex))
763
                    return;
764
                throw;
765
            }
766
            catch (Exception ex)
767
            {
768
                Log.Error("Unexpected error while uploading file", ex);
769
                throw;
770 788
            }
771

  
772 789
        }
773 790

  
774 791

  
......
810 827
            var account = cloudFile.Account ?? accountInfo.UserName;
811 828
            var container = cloudFile.Container ;
812 829

  
813
            var client = new CloudFilesClient(accountInfo);
830
            var client = new CloudFilesClient(accountInfo);            
814 831
            //Send the hashmap to the server            
815 832
            var missingHashes =  await client.PutHashMap(account, container, url, treeHash);
833
            int block = 0;
834
            ReportUploadProgress(fileInfo.Name,block++, missingHashes.Count, fileInfo.Length);
816 835
            //If the server returns no missing hashes, we are done
817 836
            while (missingHashes.Count > 0)
818 837
            {
......
834 853
                    }
835 854
                    catch (Exception exc)
836 855
                    {
837
                        Log.ErrorFormat("[ERROR] uploading block {0} of {1}\n{2}", blockIndex, fullFileName, exc);
856
                        Log.Error(String.Format("Uploading block {0} of {1}", blockIndex, fullFileName), exc);
838 857
                    }
839

  
858
                    ReportUploadProgress(fileInfo.Name,block++, missingHashes.Count, fileInfo.Length);
840 859
                }
841 860

  
842 861
                //Repeat until there are no more missing hashes                
843 862
                missingHashes = await client.PutHashMap(account, container, url, treeHash);
844 863
            }
864
            ReportUploadProgress(fileInfo.Name, missingHashes.Count, missingHashes.Count, fileInfo.Length);
845 865
        }
846 866

  
847

  
867
        private void ReportUploadProgress(string fileName,int block, int totalBlocks, long fileSize)
868
        {
869
            StatusNotification.Notify(new ProgressNotification(fileName,"Uploading",block,totalBlocks,fileSize) );            
870
        }
871
        private void ReportDownloadProgress(string fileName,int block, int totalBlocks, long fileSize)
872
        {
873
            StatusNotification.Notify(new ProgressNotification(fileName,"Downloading",block,totalBlocks,fileSize) );            
874
        }
848 875
    }
849 876

  
850 877
   

Also available in: Unified diff