From fd3eea3dc01b404b4814850889b67794fbb46d5e Mon Sep 17 00:00:00 2001
From: Appleoddity <29755504+appleoddity@users.noreply.github.com>
Date: Wed, 6 Nov 2019 11:12:04 -0500
Subject: [PATCH] Several bug fixes on Sync-SfItem
The download behavior (and upload behavior) contains several bugs and deviations from sfcli.exe. Sync-SfItem does NOT work like sfcli.exe, the documentation claiming so is all wrong. The following bugs were fixed in this patch, but apply only to downloads. The same faulty logic problems exist in the upload routines and have not been touched.
1. If 'Move' or 'Synchronize' is used, 'recursive' is no longer forced. This was a deviation from sfcli.exe
2. When 'CreateRoot' is used and the source is a ShareFile folder, Sync-SfItem would create the parent of the source folder in the destination. This was a deviation from sfcli.exe.
3. When 'CreateRoot' is not used, Sync-SfItem will still create the parent folder of the items being synchronized in the destination. This was a deviation from sfcli.exe.
4. When 'CreateRoot' is used, Sync-SfItem will now behave in one of two ways. If the source is only files, then the parent of those files will be created in the destination. If the source is a folder, the folder will be created in the destination. Otherwise, if 'CreateRoot' is not used, the parent folder is not created at all in the destination.
5. When 'Move' is used, folders would be deleted even when 'KeepFolders' was used if the folder did not have any child items. This was a bug, and a deviation from sfcli.exe.
---
ShareFileSnapIn/SyncSfItem.cs | 1702 +++++++++++++++++----------------
1 file changed, 864 insertions(+), 838 deletions(-)
diff --git a/ShareFileSnapIn/SyncSfItem.cs b/ShareFileSnapIn/SyncSfItem.cs
index 25bc51a..70d81f7 100644
--- a/ShareFileSnapIn/SyncSfItem.cs
+++ b/ShareFileSnapIn/SyncSfItem.cs
@@ -1,838 +1,864 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Management.Automation;
-using ShareFile.Api;
-using ShareFile.Api.Models;
-using System.IO;
-using ShareFile.Api.Client;
-using ShareFile.Api.Client.Transfers;
-using ShareFile.Api.Client.FileSystem;
-using ShareFile.Api.Client.Transfers.Uploaders;
-using System.Threading;
-using ShareFile.Api.Powershell.Parallel;
-using ShareFile.Api.Powershell.Log;
-using ShareFile.Api.Client.Requests;
-using ShareFile.Api.Client.Exceptions;
-using ShareFile.Api.Powershell.Properties;
-using System.Collections.ObjectModel;
-using System.Text.RegularExpressions;
-
-namespace ShareFile.Api.Powershell
-{
- ///
- /// Sync-SFItem command to sync/copy/move items between local and sharefile locations
- ///
- [Cmdlet("Sync", Noun, SupportsShouldProcess = true, DefaultParameterSetName = SetNameAttr)]
- public class SyncSfItem : PSCmdlet
- {
- #region Local Variables
-
- private const string Noun = "SfItem";
-
- // different Parameter Set Names for different type of behaviors of command
- private const string SetNameAttr = "SetAttr";
- private const string SetNamePos = "SetPos";
- private const string SetNameHelp = "SetHelp";
-
- private FileSupport FileSupport;
-
- // Keep track of abandone/resume functionality if disconnected meanwhile any operation
- private Resume.ResumeSupport ResumeSupport { get; set; }
-
- #endregion
-
- #region Command Arguments
-
- ///
- /// Sharefile location
- ///
- [Alias("SF", "ShareFile")]
- [Parameter(Mandatory = true, ParameterSetName = SetNameAttr)]
- [Parameter(Mandatory = true, Position = 0, ParameterSetName = SetNamePos)]
- public string ShareFilePath { get; set; }
-
- ///
- /// Local location
- ///
- [Alias("L", "Local", "Path")]
- [Parameter(Position = 1, ParameterSetName = SetNamePos)]
- [Parameter(Mandatory = false, ParameterSetName = SetNameAttr)]
- public string LocalPath { get; set; }
-
- ///
- /// Download param flag
- ///
- [Alias("Down", "D")]
- [Parameter()]
- public SwitchParameter Download { get; set; }
-
- ///
- /// Upload param flag
- ///
- [Alias("Up", "U")]
- [Parameter()]
- public SwitchParameter Upload { get; set; }
-
- ///
- /// Synchronize param flag
- ///
- [Alias("Sync", "S")]
- [Parameter()]
- public SwitchParameter Synchronize { get; set; }
-
- ///
- /// Recursive/Deep param flag
- ///
- [Alias("Deep", "R")]
- [Parameter()]
- public SwitchParameter Recursive { get; set; }
-
- ///
- /// Create root folder param flag
- ///
- [Alias("CR")]
- [Parameter()]
- public SwitchParameter CreateRoot { get; set; }
-
- ///
- /// Overwrite param flag
- ///
- [Alias("O")]
- [Parameter()]
- public SwitchParameter OverWrite { get; set; }
-
- ///
- /// Move (Cut+Paste) param flag
- ///
- [Alias("M")]
- [Parameter()]
- public SwitchParameter Move { get; set; }
-
- ///
- /// Keep Folders param flag (in case of move just delete files from source location)
- ///
- [Alias("KF")]
- [Parameter()]
- public SwitchParameter KeepFolders { get; set; }
-
- ///
- /// Strict param flag
- ///
- [Parameter()]
- public SwitchParameter Strict { get; set; }
-
- ///
- /// Version param flag
- ///
- [Alias("Version", "V")]
- [Parameter(ParameterSetName = SetNameHelp, Mandatory = false)]
- public SwitchParameter Help { get; set; }
-
- ///
- /// Details/Description text to upload to Sharefile location
- ///
- [Parameter()]
- public String Details { get; set; }
-
- #endregion
-
- ///
- /// Sync-SFItem Command is invoked
- ///
- protected override void ProcessRecord()
- {
- FileSupport = new FileSupport(TestMethod);
- ProviderInfo providerInfo;
- PSDriveInfo driveInfo;
-
- if (Upload && Download)
- {
- throw new PSArgumentException("Provide only one switch to Upload or Download the files");
- }
-
- if (!string.IsNullOrEmpty(ShareFilePath))
- {
- if (!Upload && !Download)
- {
- throw new PSArgumentException("Upload or Download switch must be specified");
- }
-
- if (string.IsNullOrEmpty(LocalPath) || string.IsNullOrEmpty(LocalPath.Trim()))
- {
- // use current user directory location if Local path is not specified in arguments
- LocalPath = this.SessionState.Path.CurrentFileSystemLocation.Path;
- }
-
- if (Synchronize)
- {
- Recursive = true;
- }
-
- if (Move)
- {
- Recursive = true;
- }
-
- ShareFilePath = ShareFilePath.Trim();
- LocalPath = LocalPath.Trim();
-
- ActionType actionType = OverWrite ? ActionType.Force : (Synchronize ? ActionType.Sync : ActionType.None);
-
- int transactionId = new Random((int)DateTime.Now.Ticks).Next();
-
- // if current user directory is local storage and ShareFile path provided withouth drive letter then append the drive letter with sharefile location
- if (this.SessionState.Path.CurrentLocation.Provider.ImplementingType != typeof(ShareFileProvider) && ShareFilePath.IndexOf(":") < 1)
- {
- Collection providerDrives = this.SessionState.Drive.GetAll();// ForProvider("ShareFile");
- foreach (PSDriveInfo driveObj in providerDrives)
- {
- if (driveObj.Provider.ImplementingType == typeof(ShareFileProvider))
- {
- if (ShareFilePath.StartsWith("/") || ShareFilePath.StartsWith(@"\"))
- {
- ShareFilePath = ShareFilePath.Substring(1);
- }
- string sfDrive = String.Format("{0}:/", driveObj.Name);
- ShareFilePath = Path.Combine(sfDrive, ShareFilePath);
- break;
- }
- }
- }
-
- var sourcePath = Upload ? LocalPath : ShareFilePath;
-
- // it will resolve paths if wildcards are used e.g. "D:\\*.txt" then get paths of all files with txt extension from D:\\ location
- var resolvedPaths = this.GetResolvedProviderPathFromPSPath(sourcePath, out providerInfo);
-
- if (Download)
- {
- this.SessionState.Path.GetUnresolvedProviderPathFromPSPath(sourcePath, out providerInfo, out driveInfo);
-
- var client = ((ShareFileDriveInfo)driveInfo).Client;
-
- StartDownload(client, driveInfo, resolvedPaths, actionType);
- }
- else
- {
- var unresolvedPath = this.SessionState.Path.GetUnresolvedProviderPathFromPSPath(ShareFilePath, out providerInfo, out driveInfo);
-
- var client = ((ShareFileDriveInfo)driveInfo).Client;
- Item targetItem = ShareFileProvider.GetShareFileItem((ShareFileDriveInfo)driveInfo, unresolvedPath);
-
- if (targetItem == null && !unresolvedPath.StartsWith(String.Format(@"\{0}\", Utility.DefaultSharefileFolder)))
- {
- string updatedPath = String.Format(@"\{0}\{1}", Utility.DefaultSharefileFolder, unresolvedPath);
- targetItem = ShareFileProvider.GetShareFileItem((ShareFileDriveInfo)driveInfo, updatedPath, null, null);
- }
- //else if (targetItem == null)
- //{
- // targetItem = ShareFileProvider.GetShareFileItem((ShareFileDriveInfo)driveInfo, ShareFilePath, null, null);
- //}
-
- if (targetItem == null)
- {
- throw new FileNotFoundException("Destination path not found on ShareFile server.");
- }
-
- // if user didn't specify the Sharefile HomeFolder in path then appending in path
- // e.g. if user tries sf:/Folder1 as sharefile target then resolve this path to sf:/My Files & Folders/Folder1
- if ((targetItem as Folder).Info.IsAccountRoot == true)
- {
- string updatedPath = String.Format(@"\{0}\{1}", Utility.DefaultSharefileFolder, unresolvedPath);
- targetItem = ShareFileProvider.GetShareFileItem((ShareFileDriveInfo)driveInfo, updatedPath, null, null);
- }
-
- StartUpload(client, transactionId, targetItem, resolvedPaths, actionType);
- }
-
- WriteObject("Sync operation successfully completed.");
- }
-
- if (Help)
- {
- WriteObject("SFCLI version " + Resources.Version);
- }
- }
-
- #region Download & methods
-
- ///
- /// Start download process
- ///
- private void StartDownload(ShareFileClient client, PSDriveInfo driveInfo, ICollection resolvedPaths, ActionType actionType)
- {
- int transactionId = new Random((int)DateTime.Now.Ticks).Next();
-
- ActionManager actionManager = new ActionManager(this, string.Empty);
- bool firstIteration = true;
-
- var shareFileItems = new List- ();
- foreach (string path in resolvedPaths)
- {
- var item = Utility.ResolveShareFilePath(driveInfo, path);
-
- if (item == null)
- {
- throw new FileNotFoundException(string.Format("Source path '{0}' not found on ShareFile server.", path));
- }
-
- var target = new DirectoryInfo(LocalPath);
-
- if (!target.Exists)
- {
- throw new Exception(string.Format("Destination '{0}' path not found on local drive.", LocalPath));
- }
-
- // if create root folder flag is specified then create a container folder first
- if (firstIteration && CreateRoot)
- {
- Models.Folder parentFolder = client.Items.GetParent(item.url).Execute() as Folder;
-
- target = CreateLocalFolder(target, parentFolder);
- firstIteration = false;
- }
-
- if (item is Models.Folder)
- {
- // if user downloading the root drive then download its root folders
- if ((item as Folder).Info.IsAccountRoot.GetValueOrDefault())
- {
- var children = client.Items.GetChildren(item.url)
- .Select("Id")
- .Select("url")
- .Select("FileName")
- .Select("FileSizeBytes")
- .Select("Hash")
- .Select("Info")
- .Execute();
-
- foreach (var child in children.Feed)
- {
- if (child is Models.Folder)
- {
- DownloadRecursive(client, transactionId, child, target, actionType);
-
- shareFileItems.Add(child);
- }
- }
- }
- else
- {
- DownloadRecursive(client, transactionId, item, target, actionType);
-
- shareFileItems.Add(item);
- }
- }
- else if (item is Models.File)
- {
- DownloadAction downloadAction = new DownloadAction(FileSupport, client, transactionId, (Models.File)item, target, actionType);
- actionManager.AddAction(downloadAction);
-
- shareFileItems.Add(item);
- }
- }
-
- actionManager.Execute();
-
- // if strict flag is specified then also clean the target files which are not in source
- if (Strict)
- {
- var target = new DirectoryInfo(LocalPath);
- var directories = target.GetDirectories();
-
- foreach (string path in resolvedPaths)
- {
- var item = Utility.ResolveShareFilePath(driveInfo, path);
-
- if (item is Folder)
- {
- foreach (DirectoryInfo directory in directories)
- {
- if (directory.Name.Equals(item.Name))
- {
- DeleteLocalStrictRecursive(client, item, directory);
- break;
- }
- }
- }
- }
- }
-
- // on move remove source files
- if (Move)
- {
- foreach(var item in shareFileItems)
- {
- DeleteShareFileItemRecursive(client, item, Recursive);
- }
- }
- }
-
- ///
- /// Download all items recursively
- ///
- private void DownloadRecursive(ShareFileClient client, int downloadId, Models.Item source, DirectoryInfo target, ActionType actionType)
- {
- if (source is Models.Folder)
- {
- var subdir = CreateLocalFolder(target, source as Folder);
-
- var children = client.Items.GetChildren(source.url)
- .Select("Id")
- .Select("url")
- .Select("FileName")
- .Select("FileSizeBytes")
- .Select("Hash")
- .Select("Info")
- .Execute();
-
- if (children != null)
- {
- (source as Folder).Children = children.Feed;
-
- ActionManager actionManager = new ActionManager(this, source.Name);
-
- foreach (var child in children.Feed)
- {
- child.Parent = source;
-
- if (child is Models.Folder && Recursive)
- {
- DownloadRecursive(client, downloadId, child, subdir, actionType);
- }
- else if (child is Models.File)
- {
- DownloadAction downloadAction = new DownloadAction(FileSupport, client, downloadId, (Models.File)child, subdir, actionType);
- actionManager.AddAction(downloadAction);
- }
- }
-
- actionManager.Execute();
- }
- }
- }
-
- ///
- /// Delete the Sharefile items if Move flag is used
- ///
- private void DeleteShareFileItemRecursive(ShareFileClient client, Models.Item source, bool deleteFolder)
- {
- if (source is Models.Folder)
- {
- var children = (source as Folder).Children;
- var childFiles = children.OfType();
- var childFolders = children.OfType();
-
- RemoveShareFileItems(client, source, childFiles);
-
- if (Recursive)
- {
- foreach (var childFolder in childFolders)
- {
- DeleteShareFileItemRecursive(client, childFolder, !KeepFolders);
- }
- }
-
- if (deleteFolder)
- {
- if(!HasChildren(client, source as Models.Folder))
- {
- RemoveShareFileItem(client, source);
- }
- }
- }
-
- if (source is Models.File)
- {
- RemoveShareFileItem(client, source);
- }
- }
-
- ///
- /// Clean the target folders in case of Strict flag
- ///
- private void DeleteLocalStrictRecursive(ShareFileClient client, Models.Item source, DirectoryInfo target)
- {
- var directories = target.GetDirectories();
- var children = client.Items.GetChildren(source.url).Execute();
-
- foreach (DirectoryInfo directory in directories)
- {
- bool found = false;
-
- foreach (var child in children.Feed)
- {
- if (child is Models.Folder && child.Name.Equals(directory.Name))
- {
- DeleteLocalStrictRecursive(client, child, directory);
- found = true;
- break;
- }
- }
-
- if (!found)
- {
- RemoveLocalItem(directory);
- }
- }
-
- var files = target.GetFiles();
-
- foreach (FileInfo file in files)
- {
- bool found = false;
- foreach (var child in children.Feed)
- {
- if (child is Models.File && child.Name.Equals(file.Name))
- {
- found = true;
- break;
- }
- }
-
- if (!found)
- {
- RemoveLocalItem(file);
- }
- }
- }
-
- ///
- /// Create local folder
- ///
- private DirectoryInfo CreateLocalFolder(DirectoryInfo target, Folder source)
- {
- string sourceFolderName = string.Empty;
-
- // if source is user's Home/Root folder then specify default name
- if (source.Info.IsAHomeFolder == true && source.Info.IsAStartFolder == true)
- {
- sourceFolderName = Utility.DefaultSharefileFolder;
- }
- else
- {
- sourceFolderName = source.FileName;
- }
- var subdirCheck = new DirectoryInfo(System.IO.Path.Combine(target.FullName, sourceFolderName));
-
- if (subdirCheck.Exists)
- {
- if (Synchronize)
- return subdirCheck;
-
- if (!OverWrite)
- throw new IOException("Path " + subdirCheck.FullName + " already exists. Use -Overwrite to ignore");
- }
-
- return target.CreateSubdirectory(sourceFolderName);
- }
-
- #endregion
-
- #region Upload & methods
-
- ///
- /// Start Upload to Sharefile location
- ///
- private void StartUpload(ShareFileClient client, int uploadId, Models.Item target, ICollection resolvedPaths, ActionType actionType)
- {
- int transactionId = new Random((int)DateTime.Now.Ticks).Next();
-
- ActionManager actionManager = new ActionManager(this, string.Empty);
- bool firstIteration = true;
-
- foreach (string path in resolvedPaths)
- {
- FileAttributes attr = System.IO.File.GetAttributes(path);
- FileSystemInfo source = ((attr & FileAttributes.Directory) == FileAttributes.Directory) ? new DirectoryInfo(path) : source = new FileInfo(path);
-
- // create an extra parent folder if CreateRoot flag is specified on target location
- if (firstIteration && CreateRoot)
- {
- DirectoryInfo parentFolder = Directory.GetParent(path);
- var newFolder = new Models.Folder() { Name = parentFolder.Name };
- target = client.Items.CreateFolder(target.url, newFolder, OverWrite, false).Execute();
- firstIteration = false;
- }
-
- if (source is DirectoryInfo)
- {
- UploadRecursive(client, uploadId, source, target, actionType);
- }
- else
- {
- IAction uploadAction = new UploadAction(FileSupport, client, source, target, Details, actionType);
- actionManager.AddAction(uploadAction);
- }
- }
-
- actionManager.Execute();
-
- if (Strict)
- {
- foreach (string path in resolvedPaths)
- {
- FileAttributes attr = System.IO.File.GetAttributes(path);
- if ((attr & FileAttributes.Directory) == FileAttributes.Directory)
- {
- DirectoryInfo source = new DirectoryInfo(path);
-
- var children = client.Items.GetChildren(target.url).Execute();
-
- foreach (var child in children.Feed)
- {
- if (child is Models.Folder && child.Name.Equals(source.Name))
- {
- DeleteSharefileStrictRecursive(client, source as DirectoryInfo, child);
- break;
- }
- }
- }
- }
- }
-
- if (Move)
- {
- foreach (string path in resolvedPaths)
- {
- FileAttributes attr = System.IO.File.GetAttributes(path);
- FileSystemInfo source = ((attr & FileAttributes.Directory) == FileAttributes.Directory) ? new DirectoryInfo(path) : source = new FileInfo(path);
-
- DeleteLocalItemRecursive(source, !KeepFolders);
- }
- }
- }
-
- ///
- /// Upload contents recursively
- ///
- private void UploadRecursive(ShareFileClient client, int uploadId, FileSystemInfo source, Models.Item target, ActionType actionType)
- {
- if (source is DirectoryInfo)
- {
- var newFolder = new Models.Folder() { Name = source.Name };
- bool isExist = false;
-
- if (Synchronize)
- {
- try
- {
- string path = String.Format("/{0}", source.Name);
- Item item = null;
- try
- {
- item = client.Items.ByPath(target.url, path).Execute();
- }
- catch (ODataException e)
- {
- if (e.Code != System.Net.HttpStatusCode.NotFound)
- {
- throw e;
- }
- }
-
- if (item != null && item is Folder)
- {
- isExist = true;
- newFolder = (Folder)item;
- }
- }
- catch { }
- }
-
- if (!isExist)
- {
- newFolder = client.Items.CreateFolder(target.url, newFolder, OverWrite, false).Execute();
- }
-
- ActionManager actionManager = new ActionManager(this, source.Name);
-
- foreach (var fsInfo in ((DirectoryInfo)source).EnumerateFileSystemInfos())
- {
- if (fsInfo is DirectoryInfo && Recursive)
- {
- UploadRecursive(client, uploadId, fsInfo, newFolder, actionType);
- }
- else if (fsInfo is FileInfo)
- {
- IAction uploadAction = new UploadAction(FileSupport, client, fsInfo, newFolder, Details, actionType);
- actionManager.AddAction(uploadAction);
- }
- }
-
- actionManager.Execute();
- }
- }
-
- ///
- /// Delete sharefile contents on Strict flag to make exact copy of source
- ///
- private void DeleteSharefileStrictRecursive(ShareFileClient client, DirectoryInfo source, Item target)
- {
- var children = client.Items.GetChildren(target.url).Execute();
- var directories = source.GetDirectories();
- var files = source.GetFiles();
-
- foreach (var child in children.Feed)
- {
- bool found = false;
- if (child is Models.Folder)
- {
- foreach (DirectoryInfo directory in directories)
- {
- if (directory.Name.Equals(child.Name))
- {
- DeleteSharefileStrictRecursive(client, directory, child);
- found = true;
- }
- }
- }
- else if (child is Models.File)
- {
- foreach (FileInfo file in files)
- {
- if (file.Name.Equals(child.Name))
- {
- found = true;
- break;
- }
- }
- }
-
- if (!found)
- {
- RemoveShareFileItem(client, child);
- }
- }
-
- //foreach (DirectoryInfo directory in directories)
- //{
- // foreach (var child in children.Feed)
- // {
- // if (child is Models.Folder && child.Name.Equals(directory.Name))
- // {
- // DeleteLocalStrictRecursive(client, child, directory);
- // break;
- // }
- // }
- //}
-
-
-
- //foreach (FileInfo file in files)
- //{
- // bool found = false;
- // foreach (var child in children.Feed)
- // {
- // if (child is Models.File && child.Name.Equals(file.Name))
- // {
- // found = true;
- // }
- // }
-
- // if (!found)
- // {
- // RemoveLocalItem(file);
- // }
- //}
- }
-
- ///
- /// Delete local items if Move flag is specified
- ///
- private void DeleteLocalItemRecursive(FileSystemInfo source, bool deleteFolder)
- {
- if (source is DirectoryInfo)
- {
- foreach (var child in (source as DirectoryInfo).EnumerateFileSystemInfos())
- {
- if (child is FileInfo || Recursive)
- {
- DeleteLocalItemRecursive(child, !KeepFolders);
- }
- }
- }
-
- if (source is FileInfo || deleteFolder)
- {
- RemoveLocalItem(source);
- }
- }
-
- #endregion
-
- #region Utility Methods
-
- ///
- /// Remove sharefile item
- ///
- private bool RemoveShareFileItem(ShareFileClient client, Item item)
- {
- Query query = new Query(client);
-
- query.HttpMethod = "DELETE";
- query.Id(item.Id);
- query.From("Items");
-
- try
- {
- client.Execute(query);
- }
- catch
- {
- return false;
- }
-
- return true;
- }
-
- private bool RemoveShareFileItems(ShareFileClient client, Item parentFolder, IEnumerable
- items)
- {
- try
- {
- client.Items.BulkDelete(parentFolder.url, items.Select(x => x.Id)).Execute();
- return true;
- }
- catch
- {
- return false;
- }
- }
-
- private bool HasChildren(ShareFileClient client, Folder folder)
- {
- try
- {
- var children = client.Items.GetChildren(folder.url).Top(1).Execute();
- return children.count > 0;
- }
- catch
- {
- return false;
- }
- }
-
- ///
- /// Remove local item
- ///
- private bool RemoveLocalItem(FileSystemInfo item)
- {
- item.Delete();
-
- return true;
- }
-
- ///
- /// Delegate method (which will be used for Marking file status of completed files)
- ///
- private void TestMethod(String fileName)
- {
-
- }
-
- #endregion
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Management.Automation;
+using ShareFile.Api;
+using ShareFile.Api.Models;
+using System.IO;
+using ShareFile.Api.Client;
+using ShareFile.Api.Client.Transfers;
+using ShareFile.Api.Client.FileSystem;
+using ShareFile.Api.Client.Transfers.Uploaders;
+using System.Threading;
+using ShareFile.Api.Powershell.Parallel;
+using ShareFile.Api.Powershell.Log;
+using ShareFile.Api.Client.Requests;
+using ShareFile.Api.Client.Exceptions;
+using ShareFile.Api.Powershell.Properties;
+using System.Collections.ObjectModel;
+using System.Text.RegularExpressions;
+
+namespace ShareFile.Api.Powershell
+{
+ ///
+ /// Sync-SFItem command to sync/copy/move items between local and sharefile locations
+ ///
+ [Cmdlet("Sync", Noun, SupportsShouldProcess = true, DefaultParameterSetName = SetNameAttr)]
+ public class SyncSfItem : PSCmdlet
+ {
+ #region Local Variables
+
+ private const string Noun = "SfItem";
+
+ // different Parameter Set Names for different type of behaviors of command
+ private const string SetNameAttr = "SetAttr";
+ private const string SetNamePos = "SetPos";
+ private const string SetNameHelp = "SetHelp";
+
+ private FileSupport FileSupport;
+
+ // Keep track of abandone/resume functionality if disconnected meanwhile any operation
+ private Resume.ResumeSupport ResumeSupport { get; set; }
+
+ #endregion
+
+ #region Command Arguments
+
+ ///
+ /// Sharefile location
+ ///
+ [Alias("SF", "ShareFile")]
+ [Parameter(Mandatory = true, ParameterSetName = SetNameAttr)]
+ [Parameter(Mandatory = true, Position = 0, ParameterSetName = SetNamePos)]
+ public string ShareFilePath { get; set; }
+
+ ///
+ /// Local location
+ ///
+ [Alias("L", "Local", "Path")]
+ [Parameter(Position = 1, ParameterSetName = SetNamePos)]
+ [Parameter(Mandatory = false, ParameterSetName = SetNameAttr)]
+ public string LocalPath { get; set; }
+
+ ///
+ /// Download param flag
+ ///
+ [Alias("Down", "D")]
+ [Parameter()]
+ public SwitchParameter Download { get; set; }
+
+ ///
+ /// Upload param flag
+ ///
+ [Alias("Up", "U")]
+ [Parameter()]
+ public SwitchParameter Upload { get; set; }
+
+ ///
+ /// Synchronize param flag
+ ///
+ [Alias("Sync", "S")]
+ [Parameter()]
+ public SwitchParameter Synchronize { get; set; }
+
+ ///
+ /// Recursive/Deep param flag
+ ///
+ [Alias("Deep", "R")]
+ [Parameter()]
+ public SwitchParameter Recursive { get; set; }
+
+ ///
+ /// Create root folder param flag
+ ///
+ [Alias("CR")]
+ [Parameter()]
+ public SwitchParameter CreateRoot { get; set; }
+
+ ///
+ /// Overwrite param flag
+ ///
+ [Alias("O")]
+ [Parameter()]
+ public SwitchParameter OverWrite { get; set; }
+
+ ///
+ /// Move (Cut+Paste) param flag
+ ///
+ [Alias("M")]
+ [Parameter()]
+ public SwitchParameter Move { get; set; }
+
+ ///
+ /// Keep Folders param flag (in case of move just delete files from source location)
+ ///
+ [Alias("KF")]
+ [Parameter()]
+ public SwitchParameter KeepFolders { get; set; }
+
+ ///
+ /// Strict param flag
+ ///
+ [Parameter()]
+ public SwitchParameter Strict { get; set; }
+
+ ///
+ /// Version param flag
+ ///
+ [Alias("Version", "V")]
+ [Parameter(ParameterSetName = SetNameHelp, Mandatory = false)]
+ public SwitchParameter Help { get; set; }
+
+ ///
+ /// Details/Description text to upload to Sharefile location
+ ///
+ [Parameter()]
+ public String Details { get; set; }
+
+ #endregion
+
+ ///
+ /// Sync-SFItem Command is invoked
+ ///
+ protected override void ProcessRecord()
+ {
+ FileSupport = new FileSupport(TestMethod);
+ ProviderInfo providerInfo;
+ PSDriveInfo driveInfo;
+
+ if (Upload && Download)
+ {
+ throw new PSArgumentException("Provide only one switch to Upload or Download the files");
+ }
+
+ if (!string.IsNullOrEmpty(ShareFilePath))
+ {
+ if (!Upload && !Download)
+ {
+ throw new PSArgumentException("Upload or Download switch must be specified");
+ }
+
+ if (string.IsNullOrEmpty(LocalPath) || string.IsNullOrEmpty(LocalPath.Trim()))
+ {
+ // use current user directory location if Local path is not specified in arguments
+ LocalPath = this.SessionState.Path.CurrentFileSystemLocation.Path;
+ }
+ // KA - Fix. It makes no sense why Synchronize or Move should force recursive. This is not the behavior of the original sfcli.exe.
+ /*
+ if (Synchronize)
+ {
+ Recursive = true;
+ }
+
+ if (Move)
+ {
+ Recursive = true;
+ }*/
+
+ ShareFilePath = ShareFilePath.Trim();
+ LocalPath = LocalPath.Trim();
+
+ ActionType actionType = OverWrite ? ActionType.Force : (Synchronize ? ActionType.Sync : ActionType.None);
+
+ int transactionId = new Random((int)DateTime.Now.Ticks).Next();
+
+ // if current user directory is local storage and ShareFile path provided withouth drive letter then append the drive letter with sharefile location
+ if (this.SessionState.Path.CurrentLocation.Provider.ImplementingType != typeof(ShareFileProvider) && ShareFilePath.IndexOf(":") < 1)
+ {
+ Collection providerDrives = this.SessionState.Drive.GetAll();// ForProvider("ShareFile");
+ foreach (PSDriveInfo driveObj in providerDrives)
+ {
+ if (driveObj.Provider.ImplementingType == typeof(ShareFileProvider))
+ {
+ if (ShareFilePath.StartsWith("/") || ShareFilePath.StartsWith(@"\"))
+ {
+ ShareFilePath = ShareFilePath.Substring(1);
+ }
+ string sfDrive = String.Format("{0}:/", driveObj.Name);
+ ShareFilePath = Path.Combine(sfDrive, ShareFilePath);
+ break;
+ }
+ }
+ }
+
+ var sourcePath = Upload ? LocalPath : ShareFilePath;
+
+ // it will resolve paths if wildcards are used e.g. "D:\\*.txt" then get paths of all files with txt extension from D:\\ location
+ var resolvedPaths = this.GetResolvedProviderPathFromPSPath(sourcePath, out providerInfo);
+
+ if (Download)
+ {
+ this.SessionState.Path.GetUnresolvedProviderPathFromPSPath(sourcePath, out providerInfo, out driveInfo);
+
+ var client = ((ShareFileDriveInfo)driveInfo).Client;
+
+ StartDownload(client, driveInfo, resolvedPaths, actionType);
+ }
+ else
+ {
+ var unresolvedPath = this.SessionState.Path.GetUnresolvedProviderPathFromPSPath(ShareFilePath, out providerInfo, out driveInfo);
+
+ var client = ((ShareFileDriveInfo)driveInfo).Client;
+ Item targetItem = ShareFileProvider.GetShareFileItem((ShareFileDriveInfo)driveInfo, unresolvedPath);
+
+ if (targetItem == null && !unresolvedPath.StartsWith(String.Format(@"\{0}\", Utility.DefaultSharefileFolder)))
+ {
+ string updatedPath = String.Format(@"\{0}\{1}", Utility.DefaultSharefileFolder, unresolvedPath);
+ targetItem = ShareFileProvider.GetShareFileItem((ShareFileDriveInfo)driveInfo, updatedPath, null, null);
+ }
+ //else if (targetItem == null)
+ //{
+ // targetItem = ShareFileProvider.GetShareFileItem((ShareFileDriveInfo)driveInfo, ShareFilePath, null, null);
+ //}
+
+ if (targetItem == null)
+ {
+ throw new FileNotFoundException("Destination path not found on ShareFile server.");
+ }
+
+ // if user didn't specify the Sharefile HomeFolder in path then appending in path
+ // e.g. if user tries sf:/Folder1 as sharefile target then resolve this path to sf:/My Files & Folders/Folder1
+ if ((targetItem as Folder).Info.IsAccountRoot == true)
+ {
+ string updatedPath = String.Format(@"\{0}\{1}", Utility.DefaultSharefileFolder, unresolvedPath);
+ targetItem = ShareFileProvider.GetShareFileItem((ShareFileDriveInfo)driveInfo, updatedPath, null, null);
+ }
+
+ StartUpload(client, transactionId, targetItem, resolvedPaths, actionType);
+ }
+
+ WriteObject("Sync operation successfully completed.");
+ }
+
+ if (Help)
+ {
+ WriteObject("SFCLI version " + Resources.Version);
+ }
+ }
+
+ #region Download & methods
+
+ ///
+ /// Start download process
+ ///
+ private void StartDownload(ShareFileClient client, PSDriveInfo driveInfo, ICollection resolvedPaths, ActionType actionType)
+ {
+ int transactionId = new Random((int)DateTime.Now.Ticks).Next();
+
+ ActionManager actionManager = new ActionManager(this, string.Empty);
+ bool firstIteration = true;
+
+ var shareFileItems = new List
- ();
+ foreach (string path in resolvedPaths)
+ {
+ var item = Utility.ResolveShareFilePath(driveInfo, path);
+
+ if (item == null)
+ {
+ throw new FileNotFoundException(string.Format("Source path '{0}' not found on ShareFile server.", path));
+ }
+
+ var target = new DirectoryInfo(LocalPath);
+
+ if (!target.Exists)
+ {
+ throw new Exception(string.Format("Destination '{0}' path not found on local drive.", LocalPath));
+ }
+
+ // if create root folder flag is specified then create a container folder first
+ // KA - Fix. When CreateRoot is used and the source item is a folder we should NOT create the parent of that folder.
+ // This possibly fixes another unknown scenario when the root folder is specified as the source.
+ if (firstIteration && CreateRoot && !(item is Models.Folder))
+ {
+ Models.Folder parentFolder = client.Items.GetParent(item.url).Execute() as Folder;
+
+ target = CreateLocalFolder(target, parentFolder);
+ firstIteration = false;
+ }
+
+ if (item is Models.Folder)
+ {
+ // if user downloading the root drive then download its root folders
+ // KA - Fix. We should also process only subfolders and files if CreateRoot is not used and source is a folder.
+ // This prevents DownloadRecursive from creating the parent folder in conditions where CreateRoot is not specified.
+ // Code adapted from DownloadRecursive function and processes both file sources and folder sources appropriately now.
+ // if ((item as Folder).Info.IsAccountRoot.GetValueOrDefault())
+ if ((item as Folder).Info.IsAccountRoot.GetValueOrDefault() || !CreateRoot)
+ {
+ var children = client.Items.GetChildren(item.url)
+ .Select("Id")
+ .Select("url")
+ .Select("FileName")
+ .Select("FileSizeBytes")
+ .Select("Hash")
+ .Select("Info")
+ .Execute();
+
+ if (children != null)
+ {
+ (item as Folder).Children = children.Feed;
+
+ foreach (var child in children.Feed)
+ {
+ child.Parent = item;
+
+ if (child is Models.Folder && ((item as Folder).Info.IsAccountRoot.GetValueOrDefault() || Recursive))
+ {
+ DownloadRecursive(client, transactionId, child, target, actionType);
+
+ shareFileItems.Add(child);
+ }
+ else if (child is Models.File)
+ {
+ DownloadAction downloadAction = new DownloadAction(FileSupport, client, transactionId, (Models.File)child, target, actionType);
+ actionManager.AddAction(downloadAction);
+ }
+ }
+ if (!(item as Folder).Info.IsAccountRoot.GetValueOrDefault()) { shareFileItems.Add(item); }
+ }
+ }
+ else
+ {
+ DownloadRecursive(client, transactionId, item, target, actionType);
+
+ shareFileItems.Add(item);
+ }
+ }
+ else if (item is Models.File)
+ {
+ DownloadAction downloadAction = new DownloadAction(FileSupport, client, transactionId, (Models.File)item, target, actionType);
+ actionManager.AddAction(downloadAction);
+
+ shareFileItems.Add(item);
+ }
+ }
+
+ actionManager.Execute();
+
+ // if strict flag is specified then also clean the target files which are not in source
+ if (Strict)
+ {
+ var target = new DirectoryInfo(LocalPath);
+ var directories = target.GetDirectories();
+
+ foreach (string path in resolvedPaths)
+ {
+ var item = Utility.ResolveShareFilePath(driveInfo, path);
+
+ if (item is Folder)
+ {
+ foreach (DirectoryInfo directory in directories)
+ {
+ if (directory.Name.Equals(item.Name))
+ {
+ DeleteLocalStrictRecursive(client, item, directory);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // on move remove source files
+ if (Move)
+ {
+ foreach(var item in shareFileItems)
+ {
+ // KA - Fix. Replaced 'Recursive' with '!KeepFolders'. This prevents "Move" from deleting folders even when KeepFolders is specified.
+ // This fixes the bug that causes the source folder to be deleted in all scenarios where "Move" is specified and it does not contain children.
+ // DeleteShareFileItemRecursive(client, item, Recursive);
+ DeleteShareFileItemRecursive(client, item, !KeepFolders);
+ }
+ }
+ }
+
+ ///
+ /// Download all items recursively
+ ///
+ private void DownloadRecursive(ShareFileClient client, int downloadId, Models.Item source, DirectoryInfo target, ActionType actionType)
+ {
+ if (source is Models.Folder)
+ {
+ var subdir = CreateLocalFolder(target, source as Folder);
+
+ var children = client.Items.GetChildren(source.url)
+ .Select("Id")
+ .Select("url")
+ .Select("FileName")
+ .Select("FileSizeBytes")
+ .Select("Hash")
+ .Select("Info")
+ .Execute();
+
+ if (children != null)
+ {
+ (source as Folder).Children = children.Feed;
+
+ ActionManager actionManager = new ActionManager(this, source.Name);
+
+ foreach (var child in children.Feed)
+ {
+ child.Parent = source;
+
+ if (child is Models.Folder && Recursive)
+ {
+ DownloadRecursive(client, downloadId, child, subdir, actionType);
+ }
+ else if (child is Models.File)
+ {
+ DownloadAction downloadAction = new DownloadAction(FileSupport, client, downloadId, (Models.File)child, subdir, actionType);
+ actionManager.AddAction(downloadAction);
+ }
+ }
+
+ actionManager.Execute();
+ }
+ }
+ }
+
+ ///
+ /// Delete the Sharefile items if Move flag is used
+ ///
+ private void DeleteShareFileItemRecursive(ShareFileClient client, Models.Item source, bool deleteFolder)
+ {
+ if (source is Models.Folder)
+ {
+ var children = (source as Folder).Children;
+ var childFiles = children.OfType();
+ var childFolders = children.OfType();
+
+ RemoveShareFileItems(client, source, childFiles);
+
+ if (Recursive)
+ {
+ foreach (var childFolder in childFolders)
+ {
+ // KA - Fix. If we pass in 'deleteFolder' I don't know why we don't continue to use that here
+ // This keeps the behavior of DeleteShareFileItemRecursive consistent with the deleteFolder flag
+ // DeleteShareFileItemRecursive(client, childFolder, !KeepFolders);
+ DeleteShareFileItemRecursive(client, childFolder, deleteFolder);
+ }
+ }
+
+ if (deleteFolder)
+ {
+ if(!HasChildren(client, source as Models.Folder))
+ {
+ RemoveShareFileItem(client, source);
+ }
+ }
+ }
+
+ if (source is Models.File)
+ {
+ RemoveShareFileItem(client, source);
+ }
+ }
+
+ ///
+ /// Clean the target folders in case of Strict flag
+ ///
+ private void DeleteLocalStrictRecursive(ShareFileClient client, Models.Item source, DirectoryInfo target)
+ {
+ var directories = target.GetDirectories();
+ var children = client.Items.GetChildren(source.url).Execute();
+
+ foreach (DirectoryInfo directory in directories)
+ {
+ bool found = false;
+
+ foreach (var child in children.Feed)
+ {
+ if (child is Models.Folder && child.Name.Equals(directory.Name))
+ {
+ DeleteLocalStrictRecursive(client, child, directory);
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ RemoveLocalItem(directory);
+ }
+ }
+
+ var files = target.GetFiles();
+
+ foreach (FileInfo file in files)
+ {
+ bool found = false;
+ foreach (var child in children.Feed)
+ {
+ if (child is Models.File && child.Name.Equals(file.Name))
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ RemoveLocalItem(file);
+ }
+ }
+ }
+
+ ///
+ /// Create local folder
+ ///
+ private DirectoryInfo CreateLocalFolder(DirectoryInfo target, Folder source)
+ {
+ string sourceFolderName = string.Empty;
+
+ // if source is user's Home/Root folder then specify default name
+ if (source.Info.IsAHomeFolder == true && source.Info.IsAStartFolder == true)
+ {
+ sourceFolderName = Utility.DefaultSharefileFolder;
+ }
+ else
+ {
+ sourceFolderName = source.FileName;
+ }
+ var subdirCheck = new DirectoryInfo(System.IO.Path.Combine(target.FullName, sourceFolderName));
+
+ if (subdirCheck.Exists)
+ {
+ if (Synchronize)
+ return subdirCheck;
+
+ if (!OverWrite)
+ throw new IOException("Path " + subdirCheck.FullName + " already exists. Use -Overwrite to ignore");
+ }
+
+ return target.CreateSubdirectory(sourceFolderName);
+ }
+
+ #endregion
+
+ #region Upload & methods
+
+ ///
+ /// Start Upload to Sharefile location
+ ///
+ private void StartUpload(ShareFileClient client, int uploadId, Models.Item target, ICollection resolvedPaths, ActionType actionType)
+ {
+ int transactionId = new Random((int)DateTime.Now.Ticks).Next();
+
+ ActionManager actionManager = new ActionManager(this, string.Empty);
+ bool firstIteration = true;
+
+ foreach (string path in resolvedPaths)
+ {
+ FileAttributes attr = System.IO.File.GetAttributes(path);
+ FileSystemInfo source = ((attr & FileAttributes.Directory) == FileAttributes.Directory) ? new DirectoryInfo(path) : source = new FileInfo(path);
+
+ // create an extra parent folder if CreateRoot flag is specified on target location
+ if (firstIteration && CreateRoot)
+ {
+ DirectoryInfo parentFolder = Directory.GetParent(path);
+ var newFolder = new Models.Folder() { Name = parentFolder.Name };
+ target = client.Items.CreateFolder(target.url, newFolder, OverWrite, false).Execute();
+ firstIteration = false;
+ }
+
+ if (source is DirectoryInfo)
+ {
+ UploadRecursive(client, uploadId, source, target, actionType);
+ }
+ else
+ {
+ IAction uploadAction = new UploadAction(FileSupport, client, source, target, Details, actionType);
+ actionManager.AddAction(uploadAction);
+ }
+ }
+
+ actionManager.Execute();
+
+ if (Strict)
+ {
+ foreach (string path in resolvedPaths)
+ {
+ FileAttributes attr = System.IO.File.GetAttributes(path);
+ if ((attr & FileAttributes.Directory) == FileAttributes.Directory)
+ {
+ DirectoryInfo source = new DirectoryInfo(path);
+
+ var children = client.Items.GetChildren(target.url).Execute();
+
+ foreach (var child in children.Feed)
+ {
+ if (child is Models.Folder && child.Name.Equals(source.Name))
+ {
+ DeleteSharefileStrictRecursive(client, source as DirectoryInfo, child);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (Move)
+ {
+ foreach (string path in resolvedPaths)
+ {
+ FileAttributes attr = System.IO.File.GetAttributes(path);
+ FileSystemInfo source = ((attr & FileAttributes.Directory) == FileAttributes.Directory) ? new DirectoryInfo(path) : source = new FileInfo(path);
+
+ DeleteLocalItemRecursive(source, !KeepFolders);
+ }
+ }
+ }
+
+ ///
+ /// Upload contents recursively
+ ///
+ private void UploadRecursive(ShareFileClient client, int uploadId, FileSystemInfo source, Models.Item target, ActionType actionType)
+ {
+ if (source is DirectoryInfo)
+ {
+ var newFolder = new Models.Folder() { Name = source.Name };
+ bool isExist = false;
+
+ if (Synchronize)
+ {
+ try
+ {
+ string path = String.Format("/{0}", source.Name);
+ Item item = null;
+ try
+ {
+ item = client.Items.ByPath(target.url, path).Execute();
+ }
+ catch (ODataException e)
+ {
+ if (e.Code != System.Net.HttpStatusCode.NotFound)
+ {
+ throw e;
+ }
+ }
+
+ if (item != null && item is Folder)
+ {
+ isExist = true;
+ newFolder = (Folder)item;
+ }
+ }
+ catch { }
+ }
+
+ if (!isExist)
+ {
+ newFolder = client.Items.CreateFolder(target.url, newFolder, OverWrite, false).Execute();
+ }
+
+ ActionManager actionManager = new ActionManager(this, source.Name);
+
+ foreach (var fsInfo in ((DirectoryInfo)source).EnumerateFileSystemInfos())
+ {
+ if (fsInfo is DirectoryInfo && Recursive)
+ {
+ UploadRecursive(client, uploadId, fsInfo, newFolder, actionType);
+ }
+ else if (fsInfo is FileInfo)
+ {
+ IAction uploadAction = new UploadAction(FileSupport, client, fsInfo, newFolder, Details, actionType);
+ actionManager.AddAction(uploadAction);
+ }
+ }
+
+ actionManager.Execute();
+ }
+ }
+
+ ///
+ /// Delete sharefile contents on Strict flag to make exact copy of source
+ ///
+ private void DeleteSharefileStrictRecursive(ShareFileClient client, DirectoryInfo source, Item target)
+ {
+ var children = client.Items.GetChildren(target.url).Execute();
+ var directories = source.GetDirectories();
+ var files = source.GetFiles();
+
+ foreach (var child in children.Feed)
+ {
+ bool found = false;
+ if (child is Models.Folder)
+ {
+ foreach (DirectoryInfo directory in directories)
+ {
+ if (directory.Name.Equals(child.Name))
+ {
+ DeleteSharefileStrictRecursive(client, directory, child);
+ found = true;
+ }
+ }
+ }
+ else if (child is Models.File)
+ {
+ foreach (FileInfo file in files)
+ {
+ if (file.Name.Equals(child.Name))
+ {
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (!found)
+ {
+ RemoveShareFileItem(client, child);
+ }
+ }
+
+ //foreach (DirectoryInfo directory in directories)
+ //{
+ // foreach (var child in children.Feed)
+ // {
+ // if (child is Models.Folder && child.Name.Equals(directory.Name))
+ // {
+ // DeleteLocalStrictRecursive(client, child, directory);
+ // break;
+ // }
+ // }
+ //}
+
+
+
+ //foreach (FileInfo file in files)
+ //{
+ // bool found = false;
+ // foreach (var child in children.Feed)
+ // {
+ // if (child is Models.File && child.Name.Equals(file.Name))
+ // {
+ // found = true;
+ // }
+ // }
+
+ // if (!found)
+ // {
+ // RemoveLocalItem(file);
+ // }
+ //}
+ }
+
+ ///
+ /// Delete local items if Move flag is specified
+ ///
+ private void DeleteLocalItemRecursive(FileSystemInfo source, bool deleteFolder)
+ {
+ if (source is DirectoryInfo)
+ {
+ foreach (var child in (source as DirectoryInfo).EnumerateFileSystemInfos())
+ {
+ if (child is FileInfo || Recursive)
+ {
+ DeleteLocalItemRecursive(child, !KeepFolders);
+ }
+ }
+ }
+
+ if (source is FileInfo || deleteFolder)
+ {
+ RemoveLocalItem(source);
+ }
+ }
+
+ #endregion
+
+ #region Utility Methods
+
+ ///
+ /// Remove sharefile item
+ ///
+ private bool RemoveShareFileItem(ShareFileClient client, Item item)
+ {
+ Query query = new Query(client);
+
+ query.HttpMethod = "DELETE";
+ query.Id(item.Id);
+ query.From("Items");
+
+ try
+ {
+ client.Execute(query);
+ }
+ catch
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ private bool RemoveShareFileItems(ShareFileClient client, Item parentFolder, IEnumerable
- items)
+ {
+ try
+ {
+ client.Items.BulkDelete(parentFolder.url, items.Select(x => x.Id)).Execute();
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ private bool HasChildren(ShareFileClient client, Folder folder)
+ {
+ try
+ {
+ var children = client.Items.GetChildren(folder.url).Top(1).Execute();
+ return children.count > 0;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// Remove local item
+ ///
+ private bool RemoveLocalItem(FileSystemInfo item)
+ {
+ item.Delete();
+
+ return true;
+ }
+
+ ///
+ /// Delegate method (which will be used for Marking file status of completed files)
+ ///
+ private void TestMethod(String fileName)
+ {
+
+ }
+
+ #endregion
+ }
+}