From 26b5633bf50afd583425c0acb3f512503da05e45 Mon Sep 17 00:00:00 2001 From: Akutra-Ramses Atenosis Cea Date: Sat, 8 May 2021 03:20:17 -0700 Subject: [PATCH] Adding v1.4.3.6-console codebase --- hypersync.sln | 31 ++ hypersync/Constants.cs | 13 + hypersync/Constants.g.cs | 9 + hypersync/Constants.tt | 13 + hypersync/DisplayManager.cs | 116 +++++++ hypersync/FolderPath.cs | 34 ++ hypersync/Program.cs | 204 ++++++++++++ hypersync/Properties/AssemblyInfo.cs | 36 ++ hypersync/Properties/Settings.Designer.cs | 26 ++ hypersync/Properties/Settings.settings | 5 + hypersync/SyncCopy.cs | 388 ++++++++++++++++++++++ hypersync/app.config | 6 + hypersync/hypersync.csproj | 172 ++++++++++ hypersync/hypersync.ico | Bin 0 -> 32988 bytes hypersync/syncconfig.cs | 107 ++++++ 15 files changed, 1160 insertions(+) create mode 100644 hypersync.sln create mode 100644 hypersync/Constants.cs create mode 100644 hypersync/Constants.g.cs create mode 100644 hypersync/Constants.tt create mode 100644 hypersync/DisplayManager.cs create mode 100644 hypersync/FolderPath.cs create mode 100644 hypersync/Program.cs create mode 100644 hypersync/Properties/AssemblyInfo.cs create mode 100644 hypersync/Properties/Settings.Designer.cs create mode 100644 hypersync/Properties/Settings.settings create mode 100644 hypersync/SyncCopy.cs create mode 100644 hypersync/app.config create mode 100644 hypersync/hypersync.csproj create mode 100644 hypersync/hypersync.ico create mode 100644 hypersync/syncconfig.cs diff --git a/hypersync.sln b/hypersync.sln new file mode 100644 index 0000000..0880eab --- /dev/null +++ b/hypersync.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31129.286 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "hypersync", "hypersync\hypersync.csproj", "{41CD9276-B718-4EC3-AB21-C52D8AA7D30A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {41CD9276-B718-4EC3-AB21-C52D8AA7D30A}.Debug|x64.ActiveCfg = Debug|x64 + {41CD9276-B718-4EC3-AB21-C52D8AA7D30A}.Debug|x64.Build.0 = Debug|x64 + {41CD9276-B718-4EC3-AB21-C52D8AA7D30A}.Debug|x86.ActiveCfg = Debug|x86 + {41CD9276-B718-4EC3-AB21-C52D8AA7D30A}.Debug|x86.Build.0 = Debug|x86 + {41CD9276-B718-4EC3-AB21-C52D8AA7D30A}.Release|x64.ActiveCfg = Release|x64 + {41CD9276-B718-4EC3-AB21-C52D8AA7D30A}.Release|x64.Build.0 = Release|x64 + {41CD9276-B718-4EC3-AB21-C52D8AA7D30A}.Release|x86.ActiveCfg = Release|x86 + {41CD9276-B718-4EC3-AB21-C52D8AA7D30A}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {DD844196-63C4-4B72-BA8C-66055AACA3E6} + EndGlobalSection +EndGlobal diff --git a/hypersync/Constants.cs b/hypersync/Constants.cs new file mode 100644 index 0000000..5612d98 --- /dev/null +++ b/hypersync/Constants.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace hypersync +{ + public static partial class Constants + { + + } +} diff --git a/hypersync/Constants.g.cs b/hypersync/Constants.g.cs new file mode 100644 index 0000000..dd93a01 --- /dev/null +++ b/hypersync/Constants.g.cs @@ -0,0 +1,9 @@ +using System; + +namespace hypersync +{ + public static partial class Constants + { + public static DateTime CompilationTimestampUtc { get { return new DateTime(637560563236627981L, DateTimeKind.Utc); } } + } +} \ No newline at end of file diff --git a/hypersync/Constants.tt b/hypersync/Constants.tt new file mode 100644 index 0000000..a456de3 --- /dev/null +++ b/hypersync/Constants.tt @@ -0,0 +1,13 @@ +<#@ template debug="false" hostspecific="true" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System" #> +<#@ output extension=".g.cs" #> +using System; + +namespace hypersync +{ + public static partial class Constants + { + public static DateTime CompilationTimestampUtc { get { return new DateTime(<# Write(DateTime.UtcNow.Ticks.ToString()); #>L, DateTimeKind.Utc); } } + } +} \ No newline at end of file diff --git a/hypersync/DisplayManager.cs b/hypersync/DisplayManager.cs new file mode 100644 index 0000000..ba53972 --- /dev/null +++ b/hypersync/DisplayManager.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace hypersync +{ + public class cpoint + { + public int hLeft { get; set; } + public int hTop { get; set; } + public int printed { get; set; } + public string printedtext { get; set; } + + public cpoint(int spot) + { + hLeft = 0; + hTop = Console.CursorTop + spot; + printed = 0; + printedtext = ""; + } + } + + public class DisplayManager + { + cpoint[] positions = new[] { new cpoint(0), new cpoint(1), new cpoint(2), new cpoint(4) }; // Fourth row is error/warning output + + public void WriteToConsole(string Text, int row, ConsoleColor? consoleColor = null) + { + // FYI, If this method is used more will need add clear extra + Console.SetCursorPosition(positions[row].printed, this.positions[row].hTop); + positions[row].printed += Text.Length; + positions[row].printedtext += Text; + if (!object.ReferenceEquals(null, consoleColor)) { Console.ForegroundColor = consoleColor.Value; } + Console.Write(Text); + Console.ResetColor(); + } + public void AppendToConsole(string Text, ConsoleColor? consoleColor = null) + { + if (!object.ReferenceEquals(null, consoleColor)) { Console.ForegroundColor = consoleColor.Value; } + Console.Write(Text); + Console.ResetColor(); + } + + public void AppendLineToConsole(string Text, ConsoleColor? consoleColor = null) + { + if (!object.ReferenceEquals(null, consoleColor)) { Console.ForegroundColor = consoleColor.Value; } + Console.WriteLine(Text); + Console.ResetColor(); + } + + public void hWriteToConsole(string Text, int row, ConsoleColor? consoleColor = null) + { + if (Text == positions[row].printedtext) + return; + + this.ClearExtra(positions[row].printed, Text.Length, row); + this.GoTopLeft(row); + + positions[row].printed = 0; + positions[row].printedtext = ""; + this.WriteToConsole(Text, row, consoleColor); + } + + public void ClearExtra(int lenOld, int lenNew, int row) + { + if (lenOld > lenNew) + { + int pLeft = lenNew; + int pTop = this.positions[row].hTop; + int lines; + + string pad = new String(' ', lenOld-lenNew); + + if( lenNew >= Console.BufferWidth) + { + lines = (int)(lenNew / Console.BufferWidth); + pTop += lines; + pLeft = lenNew - (lines * Console.BufferWidth); + } + + Console.SetCursorPosition(pLeft, pTop); + Console.Write(pad); + } + } + + public void GoTopLeft(int row) + { + Console.SetCursorPosition(this.positions[row].hLeft, this.positions[row].hTop); + } + + public void Clear(int row) + { + this.GoTopLeft(row); + if (this.positions[row].printed > 0) { + Console.WriteLine(new String(' ', this.positions[row].printed)); + this.positions[row].printed = 0; + this.GoTopLeft(row); + } + } + + public void WriteError(string Text) + { + Console.BackgroundColor = ConsoleColor.DarkRed; + this.hWriteToConsole(Text, 3, ConsoleColor.White); + } + + public void WriteWarning(string Text) + { + Console.BackgroundColor = ConsoleColor.DarkYellow; + this.hWriteToConsole(Text, 3, ConsoleColor.White); + } + + } +} diff --git a/hypersync/FolderPath.cs b/hypersync/FolderPath.cs new file mode 100644 index 0000000..f1edf8a --- /dev/null +++ b/hypersync/FolderPath.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace hypersync +{ + public enum ptype { normal = 0 }; + public class FolderPath + { + public string src_folder; + public string dest_folder; + public ptype thisType; + } + + public class reportdata + { + public long total_items = 0; + public long total_updated = 0; + public long total_older = 0; + public long total_newer = 0; + public long total_missingdest = 0; + public long total_missingsrc = 0; + public long total_invalid = 0; + public long total_removed = 0; + public long total_zerosize = 0; + + public void reset() + { + total_items = 0; total_invalid = 0; total_missingdest = 0; total_missingsrc = 0; total_newer = 0; total_older = 0; total_updated = 0; total_removed = 0; total_zerosize = 0; + } + } +} + diff --git a/hypersync/Program.cs b/hypersync/Program.cs new file mode 100644 index 0000000..0be57f8 --- /dev/null +++ b/hypersync/Program.cs @@ -0,0 +1,204 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; + +namespace hypersync +{ + static class Program + { + static DisplayManager OutputManager = new DisplayManager(); + static configLoader ConfigManager = new configLoader(OutputManager); + static bool SaveAndExit = false; + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main(string[] args) + { + //process switch, set property value + if (args.Length > 0) + { + int argpos = 1; + foreach(string arg in args) + { + if (arg.Length > 0) { ProcessArgument(arg, argpos); } + argpos++; + } + if(SaveAndExit) + { + ConfigManager.WriteConfig(); + OutputManager.AppendToConsole("Config Stored.", ConsoleColor.White); + System.Environment.Exit(0); + } + } + if (ConfigManager.ConfigData.SyncPaths.Length > 0) + { + if (string.IsNullOrEmpty(ConfigManager.ConfigData.SyncPaths[0].dest_folder)) + { + OutputManager.WriteError("Destination path is required."); + System.Environment.Exit(ConfigManager.ERROR_BAD_ARGUMENTS); + } + if (string.IsNullOrEmpty(ConfigManager.ConfigData.SyncPaths[0].src_folder)) + { + OutputManager.WriteError("Source path is required."); + System.Environment.Exit(ConfigManager.ERROR_BAD_ARGUMENTS); + } + } else + { + OutputManager.WriteError("Both source and destination paths are required."); + System.Environment.Exit(ConfigManager.ERROR_BAD_ARGUMENTS); + } + if (ConfigManager.ConfigData.SyncPaths[0].dest_folder.Contains(ConfigManager.ConfigData.SyncPaths[0].src_folder)) + { + OutputManager.WriteError("Destination cannot be inside source due to recursion."); + System.Environment.Exit(ConfigManager.ERROR_BAD_ARGUMENTS); + } + + // Begin the File Sync. + SyncCopy Synchronizer = new SyncCopy(ConfigManager, OutputManager); + Synchronizer.SourcePath = ConfigManager.ConfigData.SyncPaths[0].src_folder; + Synchronizer.DestPath = ConfigManager.ConfigData.SyncPaths[0].dest_folder; + Synchronizer.CopyFolder(); + + string final_notice = string.Format("> Synchronization Completed at {0}\n\r", DateTime.Now); + OutputManager.Clear(3); OutputManager.Clear(2); OutputManager.Clear(1); + OutputManager.hWriteToConsole(final_notice, 0, ConsoleColor.Green); + Synchronizer.LogOutput(final_notice,1); + + OutputManager.AppendToConsole(" Total processed: "); OutputManager.AppendLineToConsole(Synchronizer.result_data.total_items.ToString().Trim(), ConsoleColor.White); + OutputManager.AppendToConsole(" Total updated: "); OutputManager.AppendLineToConsole(Synchronizer.result_data.total_updated.ToString().Trim(), ConsoleColor.White); + OutputManager.AppendToConsole(" Total older than source: "); OutputManager.AppendLineToConsole(Synchronizer.result_data.total_older.ToString().Trim(), ConsoleColor.White); + OutputManager.AppendToConsole(" Total newer than source: "); OutputManager.AppendLineToConsole(Synchronizer.result_data.total_newer.ToString().Trim(), ConsoleColor.White); + OutputManager.AppendToConsole("Total missing from destination: "); OutputManager.AppendLineToConsole(Synchronizer.result_data.total_missingdest.ToString().Trim(), ConsoleColor.White); + OutputManager.AppendToConsole(" Total missing from source: "); OutputManager.AppendLineToConsole(Synchronizer.result_data.total_missingsrc.ToString().Trim(), ConsoleColor.White); + OutputManager.AppendToConsole(" Total invalid on destination: "); OutputManager.AppendLineToConsole(Synchronizer.result_data.total_removed.ToString().Trim(), ConsoleColor.White); + OutputManager.AppendToConsole(" Total Zero Size Skipped: "); OutputManager.AppendLineToConsole(Synchronizer.result_data.total_zerosize.ToString().Trim(), ConsoleColor.White); + OutputManager.AppendToConsole(""); + } + + static string[] SplitParm(string argument) + { + string[] rt = new[] { argument, "" }; + + if (argument.Contains("=")) + { + try + { + rt[0] = argument.Split('=')[0]; + rt[1] = argument.Substring(rt[0].Length + 1); + } catch(Exception ex) + { + OutputManager.WriteError(string.Format("Argument parsing error with exception: {0}", ex.Message)); + } + } + return rt; + } + + static void ProcessArgument(string argument, int apos) + { + string[] parts = SplitParm(argument); + + string field = parts[0].Trim(new[] { '-', '/' }).ToLower(); + string parm = parts[1]; + int LogLevel; + + switch(field) + { + case "log": + ConfigManager.ConfigData.LogAction = true; + if (!string.IsNullOrEmpty(parm)) { ConfigManager.ConfigData.LogFile = parm; } + break; + case "loglevel": + ConfigManager.ConfigData.LogAction = true; + if (int.TryParse(parm, out LogLevel)) { ConfigManager.ConfigData.LogLevel = LogLevel; } + break; + case "keepnewerdest": + ConfigManager.ConfigData.KeepNewerDest = true; + break; + case "damaged": + ConfigManager.ConfigData.DamagedSource = true; + break; + case "src": + case "s": + AddSrc(parm); + break; + case "d": + AddTo(parm); + break; + case "type": + AddType(parm); + break; + case "v": + case "version": + OutputManager.AppendLineToConsole(string.Format("Hyper-Synchronizer [{0}] version {1}-console", Constants.CompilationTimestampUtc.ToLocalTime().ToLongDateString(), Application.ProductVersion), ConsoleColor.White); + System.Environment.Exit(0); + break; + case "h": + case "help": + OutputManager.AppendLineToConsole("Command Line Syntax:\n\r"); + OutputManager.AppendToConsole("\thypersync [s=]"); OutputManager.AppendToConsole("",ConsoleColor.White); + OutputManager.AppendToConsole(" [d=]"); OutputManager.AppendToConsole("", ConsoleColor.White); + OutputManager.AppendToConsole(" [type="); OutputManager.AppendToConsole("", ConsoleColor.White); OutputManager.AppendToConsole("]"); + OutputManager.AppendToConsole(" [log="); OutputManager.AppendToConsole("", ConsoleColor.White); OutputManager.AppendToConsole("]"); + OutputManager.AppendToConsole(" [loglevel="); OutputManager.AppendToConsole("", ConsoleColor.White); OutputManager.AppendToConsole("]"); + OutputManager.AppendLineToConsole(" [KeepNewerDest] [damaged] [v or version] [h or help] [w]"); + System.Environment.Exit(0); + break; + case "w": + SaveAndExit = true; + break; + default: + // No field name specified + if(apos==1) { AddSrc(argument); } + if(apos==2) { AddTo(argument); } + break; + } + } + + static void AddTo(string destination) + { + if( ConfigManager.ConfigData.SyncPaths.Length<1) + { + ConfigManager.ConfigData.SyncPaths = new[] { new FolderPath() }; + } + ConfigManager.ConfigData.SyncPaths[0].dest_folder = destination; + } + + static void AddSrc(string source) + { + if (ConfigManager.ConfigData.SyncPaths.Length < 1) + { + ConfigManager.ConfigData.SyncPaths = new[] { new FolderPath() }; + } + ConfigManager.ConfigData.SyncPaths[0].src_folder = source; + } + + static void AddType(string type) + { + List types = new List(Enum.GetNames(typeof(ptype)).Cast().Select(x => x.ToString())); + + if (ConfigManager.ConfigData.SyncPaths.Length < 1) + { + ConfigManager.ConfigData.SyncPaths = new[] { new FolderPath() }; + } + try + { + if (types.Contains(type)) + { + ConfigManager.ConfigData.SyncPaths[0].thisType = (ptype)Enum.Parse(typeof(ptype), type); + } else + { + string available = string.Join(", ", types.ToArray()); + OutputManager.WriteError(string.Format("Invalid path type specified '{0}'. Available type(s): {1}.", type, available)); + System.Environment.Exit(ConfigManager.ERROR_BAD_ARGUMENTS); + } + } + catch(Exception ex) { + OutputManager.WriteError(string.Format("Parsing parameter Type failed with exception {0}", ex.Message)); + System.Environment.Exit(ConfigManager.ERROR_BAD_ARGUMENTS); + } + } + } +} diff --git a/hypersync/Properties/AssemblyInfo.cs b/hypersync/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..f1d3ec8 --- /dev/null +++ b/hypersync/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Hyperconnect Syncronizer")] +[assembly: AssemblyDescription("Hyperconnect Syncronizer")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("LeapMaker")] +[assembly: AssemblyProduct("Hypersync")] +[assembly: AssemblyCopyright("Copyright 2012-2021 Leapmaker LLC")] +[assembly: AssemblyTrademark("Acute")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("52fe458c-f914-4318-8865-97e03bab212a")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.4.3.6")] +[assembly: AssemblyFileVersion("1.4.3.6")] diff --git a/hypersync/Properties/Settings.Designer.cs b/hypersync/Properties/Settings.Designer.cs new file mode 100644 index 0000000..5c642c8 --- /dev/null +++ b/hypersync/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace hypersync.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.8.1.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/hypersync/Properties/Settings.settings b/hypersync/Properties/Settings.settings new file mode 100644 index 0000000..8e615f2 --- /dev/null +++ b/hypersync/Properties/Settings.settings @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/hypersync/SyncCopy.cs b/hypersync/SyncCopy.cs new file mode 100644 index 0000000..890a5b6 --- /dev/null +++ b/hypersync/SyncCopy.cs @@ -0,0 +1,388 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace hypersync +{ + public class SyncCopy + { + public reportdata result_data = new reportdata(); + private DisplayManager ScreenPrinter; + private configLoader configHandler { get; set; } + + string[] exclusionList = { "$RECYCLE.BIN", "System Volume Information", "WindowsImageBackup", "RECYCLER" }; + string[] exclusionFileList = { "container.dat", "Thumbs.db", "Desktop.ini", "~$" }; + private bool StopCopy = false, skipFile = false, AnalyzeOnly = false; + public string DestPath = ""; + public string SourcePath = ""; + public char folder_delimiter = '\\'; + + public SyncCopy(configLoader settings, DisplayManager displayManager = null) + { + this.configHandler = settings; + if (object.ReferenceEquals(null, displayManager)) + { + this.ScreenPrinter = new DisplayManager(); + } else + { + this.ScreenPrinter = displayManager; + } + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + folder_delimiter = '/'; + } + } + + private string SanitizeSource(string raw_src) + { + string tsource = raw_src; + + //Remove starting drive specific chars from source path and file + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + if (tsource.Contains(":")) { tsource = tsource.Substring(tsource.IndexOf(":") + 1); } + } + if (tsource[0] == folder_delimiter) { tsource = tsource.Substring(1); } + return tsource; + } + + private string SanitizeDestination(string raw_dest, string src_root) + { + string tdest = raw_dest; + + //Remove starting drive specific chars from source path and file + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + if (tdest.Contains(":")) { tdest = tdest.Substring(tdest.IndexOf(":") + 1); } + } + if (tdest[0] == folder_delimiter) { tdest = tdest.Substring(1); } + + //Remove root folder from path + if (tdest.StartsWith(src_root)) { tdest = tdest.Substring(src_root.Length); } + if (tdest[0] == folder_delimiter) { tdest = tdest.Substring(1); } + + return tdest; + } + + private bool NoCopyAttributes(FileAttributes fAttrib) + { + return ((fAttrib & FileAttributes.Hidden) == FileAttributes.Hidden) || ((fAttrib & FileAttributes.System) == FileAttributes.System); + } + + private bool NeedToCopyValidator(string cFile, string locOnDest) + { + FileInfo dest_finfo = new FileInfo(locOnDest); + FileInfo src_finfo = new FileInfo(cFile); + + FileAttributes fattributes = File.GetAttributes(cFile); + if (NoCopyAttributes(fattributes)) + { + return false; + } + + // Statistics + if (File.GetLastWriteTime(cFile) > File.GetLastWriteTime(locOnDest)) + { + result_data.total_older++; + } else + { + result_data.total_newer++; + } + if (dest_finfo.Length < 1) { result_data.total_invalid++; } + + // Validation + if (File.GetLastWriteTime(cFile).Equals(File.GetLastWriteTime(locOnDest)) + && (dest_finfo.Length == src_finfo.Length)) { return false; } + if (File.GetLastWriteTime(cFile) < File.GetLastWriteTime(locOnDest) + && this.configHandler.ConfigData.KeepNewerDest) { return false; } + //if (dest_finfo.Length != src_finfo.Length) { return true; } + return true; + } + + public void CopyFolder(string SourceFolder = "") + { + string tsrc = "", tdst= "", locOnDest, tsource; + string[] SourceFiles; + DateTime lastWriteTime; + bool copyCurFile; + + if(SourceFolder == string.Empty) { SourceFolder = SourcePath; } + + try + { + SourceFiles = Directory.GetFiles(SourceFolder); + ScreenPrinter.hWriteToConsole("Scanning...",0); + foreach (string cFile in SourceFiles) + { + copyCurFile = true; + lastWriteTime = File.GetLastWriteTime(cFile); + tsrc = cFile; + if (cFile.Length > 0 && !StopCopy && !exclusionCheck(cFile, true)) + { + result_data.total_items++; + //Get destination path + locOnDest = DestPath; + tsource = SanitizeSource(SourcePath); + + //append destination + if (locOnDest[locOnDest .Length - 1] != folder_delimiter) { locOnDest += folder_delimiter; } + tdst = SanitizeDestination(cFile, tsource); + if( object.ReferenceEquals(null, tdst)) + { + ScreenPrinter.WriteError(string.Format("Destination path issue: {0}", tdst)); + System.Environment.Exit(0); + } + locOnDest += tdst; + + if(cFile.Length>120) + { + Console.Write(""); + } + //update display + ScreenPrinter.hWriteToConsole(cFile,1, ConsoleColor.White); + + //check for destination + copyCurFile = true; + if (File.Exists(locOnDest)) + { + copyCurFile = NeedToCopyValidator(cFile, locOnDest); + } + else + { + result_data.total_missingdest++; + } + + if (copyCurFile) + { + if (!AnalyzeOnly) + { + LogOutput("Starting Copy " + cFile + " to " + locOnDest + " @ " + DateTime.Now); + SafeCopy(cFile, locOnDest); + } + else if (AnalyzeOnly && this.configHandler.ConfigData.LogAction) + { + LogOutput("Synchronization would update:", 1); + LogFileData(cFile, locOnDest); + } + } + + } + + if (StopCopy) { break; } + } + } + catch (Exception _e) + { + if (tsrc.Length > 0) + { + ScreenPrinter.WriteError(string.Format("Path/File issue with exception {0}",_e.Message)); + } + } + + // Copy the subfolders + if (!StopCopy) + { + ScanFolders(SourceFolder); + } + } + + private void ScanFolders(string SourceFolder) + { + string[] SourceFolders; + string tsrc = ""; + try + { + SourceFolders = Directory.GetDirectories(SourceFolder); + foreach (string cFolder in SourceFolders) + { + if (!exclusionCheck(cFolder)) + { + + tsrc = cFolder; + if (cFolder.Length > 0) + { + if( !NoCopyAttributes(File.GetAttributes(cFolder))) { + CopyFolder(cFolder); } + } + } + } + } + catch (Exception _e) + { + if (tsrc.Length > 0) + { + ScreenPrinter.WriteError(_e.Message); + LogOutput("Error : " + _e.Message + " @ " + DateTime.Now, 1); + } + } + } + + public void LogOutput(string outstr, int loglevel = 3) + { + if (loglevel <= this.configHandler.ConfigData.LogLevel) { return; } + if (this.configHandler.ConfigData.LogAction) + { + StreamWriter outfle; + + if (File.Exists(this.configHandler.ConfigData.LogFile)) + { + outfle = File.AppendText(this.configHandler.ConfigData.LogFile); + } + else + { + outfle = File.CreateText(this.configHandler.ConfigData.LogFile); + } + outfle.WriteLine(outstr); + + outfle.Close(); + } + } + + void LogFileData(string src_file, string dest_file) + { + FileAttributes fattributes = File.GetAttributes(src_file); + FileInfo dest_finfo = null, src_finfo = null; + string tlog = ""; + + if (File.Exists(src_file)) + { + src_finfo = new FileInfo(src_file); + tlog = "Source File: " + src_file + " " + src_finfo.Length + " " + File.GetLastWriteTime(src_file); + } + else + { + tlog = "Source File: " + src_file + " not located."; + } + + tlog += " "; + + if (File.Exists(dest_file)) + { + dest_finfo = new FileInfo(dest_file); + tlog += "Destination File: " + dest_file + " " + dest_finfo.Length + " " + File.GetLastWriteTime(dest_file) + " "; + } + else + { + tlog += "Destination File: " + dest_file + " not located."; + } + + tlog += "@ " + DateTime.Now; + LogOutput(tlog, 1); + } + + bool SafeCopy(string src_file, string dest_file) + { + bool rt = false; + FileInfo fInfo; + + do + { + try + { + //Skip some work files, no need to replicate office work files. + fInfo = new FileInfo(src_file); + if( fInfo.Length<1 ) + { + LogOutput("processing Zero Size file skipped : " + fInfo.Name, 2); + ScreenPrinter.hWriteToConsole("Skipping zero size file...", 0); + result_data.total_zerosize++; + rt = false; + break; + } + if (fInfo.Name.StartsWith("~$")) + { + LogOutput("processing WorkFile skipped : " + fInfo.Name, 2); + ScreenPrinter.hWriteToConsole("Skipping work file..." + fInfo.Name,0); + rt = false; + break; + } + + ScreenPrinter.hWriteToConsole(dest_file,2, ConsoleColor.Yellow); + ScreenPrinter.hWriteToConsole("in progress...",0); + + //Make sure directory exists + fInfo = new FileInfo(dest_file); + if (!fInfo.Directory.Exists) { fInfo.Directory.Create(); } + + //File.Delete(dest_file); + File.Copy(src_file, dest_file, true); + result_data.total_updated++; + + ScreenPrinter.WriteToConsole("success...", 0, ConsoleColor.Green); + //ScreenPrinter.Clear(2); + LogOutput("Successfully Copied File : " + src_file + " to " + dest_file + " @ " + DateTime.Now, 2); + rt = true; + break; + } + catch (IOException _e) + { + ScreenPrinter.WriteError(_e.Message); + LogOutput(_e.ToString() + " : " + _e.Message + " @ " + DateTime.Now, 1); + if (!this.configHandler.ConfigData.DamagedSource) + { + rt = false; + break; + } + if (this.configHandler.ConfigData.DamagedSource || StopCopy || skipFile) { ScreenPrinter.hWriteToConsole("in progress...Skipping file...",0); break; } + } + catch (UnauthorizedAccessException _e) + { + //Read or write error, skip file. + ScreenPrinter.WriteError(_e.Message); + ScreenPrinter.hWriteToConsole("in progress...Skipping file...",0); + LogOutput(_e.ToString() + " : " + _e.Message + " @ " + DateTime.Now, 1); + break; + } + catch (Exception _e) + { + ScreenPrinter.WriteError(_e.Message); + ScreenPrinter.hWriteToConsole("in progress...Skipping file...",0); + LogOutput(_e.ToString() + " : " + _e.Message + " @ " + DateTime.Now, 1); + //Thread.Sleep(5000); + break; + } + if (skipFile) { break; } + } while (true); + skipFile = false; + return rt; + } + + bool exclusionCheck(string file) + { + bool rt = false; + + for (int x=0; x < exclusionList.Length; x++) // Fastest loop mechanizm for this repetative task + { + if (file.Contains(exclusionList[x])) + { + rt = true; + break; + } + } + + return rt; + } + + bool exclusionCheck(string file, bool file_chk) + { + bool rt = false; + + for (int x = 0; x < exclusionFileList.Length; x++) // Fastest loop mechanizm for this repetative task + { + if (file.Contains(exclusionFileList[x])) + { + rt = true; + break; + } + } + + return rt; + } + + } +} diff --git a/hypersync/app.config b/hypersync/app.config new file mode 100644 index 0000000..7925cc0 --- /dev/null +++ b/hypersync/app.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/hypersync/hypersync.csproj b/hypersync/hypersync.csproj new file mode 100644 index 0000000..007f5e4 --- /dev/null +++ b/hypersync/hypersync.csproj @@ -0,0 +1,172 @@ + + + + Debug + x86 + 8.0.30703 + 2.0 + {41CD9276-B718-4EC3-AB21-C52D8AA7D30A} + Exe + Properties + hypersync + Hypersync + v4.8 + + + 512 + false + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + http://www.leapmaker.com + Hypersync + Akutra Ramses Atenosis Cea + Hyperconnect + 6 + 1.4.3.%2a + false + true + + + x86 + true + full + false + bin\Win\x86\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + x86 + pdbonly + true + bin\Win\x86\Release\ + TRACE + prompt + 4 + false + + + false + + + false + + + + + + + FAB74B8371DBD037D8180DC20BB91C2C5DCD5983 + + + hypersync_TemporaryKey.pfx + + + hypersync.ico + + + hypersync.Program + + + true + bin\Win\x64\Debug\ + DEBUG;TRACE + full + x64 + 7.3 + prompt + false + + + bin\Win\x64\Release\ + TRACE + true + pdbonly + x64 + 7.3 + prompt + false + + + + + + + + + + + + + + + + True + True + Constants.tt + + + + + + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + + + + TextTemplatingFileGenerator + Constants.g.cs + + + + + + + + \ No newline at end of file diff --git a/hypersync/hypersync.ico b/hypersync/hypersync.ico new file mode 100644 index 0000000000000000000000000000000000000000..80118363cb34d158da63a78a5de7aec15fc4f27d GIT binary patch literal 32988 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