public SyncLogEntry(FileEntryType entryType, string decrFileName, DateTime decrModified, string encrFileName, DateTime encrModified) { if (string.IsNullOrWhiteSpace(decrFileName)) { throw new ArgumentNullException(nameof(decrFileName)); } if (string.IsNullOrWhiteSpace(encrFileName)) { throw new ArgumentNullException(nameof(encrFileName)); } if (!HelixUtil.IsValidPath(decrFileName)) { throw new ArgumentOutOfRangeException(nameof(decrFileName), "Invalid Path '" + decrFileName + "'"); } if (!HelixUtil.IsValidPath(encrFileName)) { throw new ArgumentOutOfRangeException(nameof(encrFileName), "Invalid Path '" + encrFileName + "'"); } this.EntryType = entryType; this.DecrFileName = HelixUtil.PathUniversal(decrFileName); if (entryType != FileEntryType.File) { this.DecrModified = DateTime.MinValue; } else { this.DecrModified = HelixUtil.TruncateTicks(decrModified); } this.EncrFileName = HelixUtil.PathUniversal(encrFileName); this.EncrModified = HelixUtil.TruncateTicks(encrModified); }
public static FileEntry FromFile(string fullPath, string root) { root = HelixUtil.PathNative(root); fullPath = HelixUtil.PathNative(fullPath); string casedFullPath = HelixUtil.GetExactPathName(fullPath); FileEntry fileInfo = new FileEntry { FileName = HelixUtil.PathUniversal(HelixUtil.RemoveRootFromPath(fullPath, root)) }; var fi = new FileInfo(casedFullPath); //Check to ensure ends with to address case changes if (fi.Exists && fi.FullName.EndsWith(fullPath, StringComparison.Ordinal)) { fileInfo.LastWriteTimeUtc = HelixUtil.TruncateTicks(fi.LastWriteTimeUtc); fileInfo.Length = fi.Length; fileInfo.EntryType = FileEntryType.File; return(fileInfo); } var di = new DirectoryInfo(casedFullPath); if (di.Exists && di.FullName.EndsWith(fullPath, StringComparison.Ordinal)) { fileInfo.LastWriteTimeUtc = DateTime.MinValue; fileInfo.EntryType = FileEntryType.Directory; return(fileInfo); } fileInfo.LastWriteTimeUtc = DateTime.MinValue; fileInfo.EntryType = FileEntryType.Removed; return(fileInfo); }
public override string ToString() { return(string.Format("{0} {1} {2} {3} {4}", EntryTypeFlag, (DecrModified == DateTime.MinValue ? dateZero : DecrModified.ToString(dateFormat)), HelixUtil.Quote(DecrFileName), (EncrModified == DateTime.MinValue ? dateZero : EncrModified.ToString(dateFormat)), HelixUtil.Quote(EncrFileName))); }
private void EncrWatcher_Changed(object sender, FileSystemEventArgs e) { if (e.ChangeType == WatcherChangeTypes.Deleted) { Enqueue(new HelixSync.DirectoryChange(PairSide.Encrypted, HelixUtil.RemoveRootFromPath(e.FullPath, DecrDirectory))); } else if (e.ChangeType == WatcherChangeTypes.Created || e.ChangeType == WatcherChangeTypes.Changed) { Enqueue(new HelixSync.DirectoryChange(PairSide.Encrypted, HelixUtil.RemoveRootFromPath(e.FullPath, DecrDirectory))); } else if (e.ChangeType == WatcherChangeTypes.Renamed) { Enqueue(new HelixSync.DirectoryChange(PairSide.Encrypted, HelixUtil.RemoveRootFromPath((e as RenamedEventArgs).OldFullPath, DecrDirectory))); Enqueue(new HelixSync.DirectoryChange(PairSide.Encrypted, HelixUtil.RemoveRootFromPath(e.FullPath, DecrDirectory))); } }
public static SyncLogEntry TryParseFromString(string line) { var match = lineParse.Match(line); if (!match.Success) { return(null); } var typeFlag = match.Groups["Type"].Value; DateTime decrModified; if (match.Groups["DecrModified"].Value == dateZero) { decrModified = DateTime.MinValue; } else if (!DateTime.TryParseExact(match.Groups["DecrModified"].Value, dateFormat, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out decrModified)) { return(null); } string decrFileName = HelixUtil.Unquote(match.Groups["DecrFileName"].Value); DateTime encrModified; if (match.Groups["EncrModified"].Value == dateZero) { encrModified = DateTime.MinValue; } else if (!DateTime.TryParseExact(match.Groups["EncrModified"].Value, dateFormat, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out encrModified)) { return(null); } string encrFileName = HelixUtil.Unquote(match.Groups["EncrFileName"].Value); return(new SyncLogEntry { DecrFileName = HelixUtil.PathUniversal(decrFileName), DecrModified = HelixUtil.TruncateTicks(decrModified), EncrFileName = HelixUtil.PathUniversal(encrFileName), EncrModified = HelixUtil.TruncateTicks(encrModified), EntryTypeFlag = typeFlag, }); }
/// <summary> /// Loads the EncrHeaders for new and updated encripted files /// </summary> private void FindChanges_St05_RefreshEncrHeaders(ConsoleEx console, List <ChangeBuilder> matchesA) { int statsRefreshHeaderCount = 0; foreach (ChangeBuilder preSyncDetails in matchesA.Where(m => m.EncrInfo != null && m.EncrInfo.LastWriteTimeUtc != m.LogEntry?.EncrModified)) { string encrFullPath = Path.Combine(EncrDirectory.FullName, HelixUtil.PathNative(preSyncDetails.EncrFileName)); if (File.Exists(encrFullPath)) { preSyncDetails.EncrHeader = HelixFile.DecryptHeader(encrFullPath, this.DerivedBytesProvider); //Updates the DecrFileName (if necessary) if (string.IsNullOrEmpty(preSyncDetails.DecrFileName) && FileNameEncoder.EncodeName(preSyncDetails.EncrHeader.FileName) == preSyncDetails.EncrInfo.RelativePath) { preSyncDetails.DecrFileName = preSyncDetails.EncrHeader.FileName; } } statsRefreshHeaderCount++; } console?.WriteLine(VerbosityLevel.Diagnostic, 2, $"Updated {statsRefreshHeaderCount} headers"); }
public SyncResults TrySync(PreSyncDetails entry, ConsoleEx console = null) { const int encrTimespanPrecisionMS = 1000; //todo: ensure the entry direction and operation end up being the same //todo: ensure all the calling methods do something with the results //if (WhatIf) // throw new InvalidOperationException("Unable to perform sync when WhatIf mode is set to true"); if (entry == null) { throw new ArgumentNullException(nameof(entry)); } if (entry.SyncMode == PreSyncMode.DecryptedSide) { SyncLogEntry logEntry = entry.LogEntry; string encrPath = Path.Combine(EncrDirectory.FullName, HelixUtil.PathNative(entry.EncrFileName)); string decrPath = Path.Combine(DecrDirectory.FullName, HelixUtil.PathNative(entry.DecrFileName)); FileEncryptOptions options = new FileEncryptOptions(); FileEntry header = null; options.BeforeWriteHeader = (h) => header = h; options.StoredFileName = entry.DecrFileName; options.FileVersion = Header.FileVersion; options.Log = (s) => console?.WriteLine(VerbosityLevel.Diagnostic, 1, s); if (WhatIf) { EncrDirectory.WhatIfReplaceFile(encrPath, entry.DisplayFileLength); if (entry.DecrInfo == null) { header = new FileEntry() { EntryType = FileEntryType.Removed, FileName = entry.DecrFileName, }; } else { header = entry.DecrInfo.ToFileEntry(); } } else { HelixFile.Encrypt(decrPath, encrPath, DerivedBytesProvider, options); //forces a change if the file was modified to quickly if (logEntry != null && (File.GetLastWriteTimeUtc(encrPath) - logEntry.EncrModified).TotalMilliseconds < encrTimespanPrecisionMS) { File.SetLastWriteTimeUtc(encrPath, logEntry.EncrModified + TimeSpan.FromMilliseconds(encrTimespanPrecisionMS)); } EncrDirectory.RefreshEntry(encrPath); } var newLogEntry = CreateEntryFromHeader(header, EncrDirectory.TryGetEntry(entry.EncrFileName)); SyncLog.Add(newLogEntry); return(SyncResults.Success()); } else if (entry.SyncMode == PreSyncMode.EncryptedSide) { if (entry.DisplayOperation == PreSyncOperation.Purge) { SyncLog.Add(entry.GetUpdatedLogEntry()); return(SyncResults.Success()); } else { //todo: use the DisplayOperation to determine what to do (ensures the counts stay consistant) SyncLogEntry logEntry = entry.LogEntry; SyncLogEntry fileSystemEntry = CreateNewLogEntryFromEncrPath(entry.EncrFileName); if (logEntry?.ToString() == fileSystemEntry?.ToString()) { return(SyncResults.Success()); //Unchanged } //todo: test to see if there are illegal characters //todo: check if the name matches string encrPath = HelixUtil.JoinNative(EncrDirectory.FullName, fileSystemEntry.EncrFileName); string decrPath = HelixUtil.JoinNative(DecrDirectory.FullName, fileSystemEntry.DecrFileName); //todo: if file exists with different case - skip file var exactPath = HelixUtil.GetExactPathName(decrPath); FSEntry decrEntry = DecrDirectory.TryGetEntry(fileSystemEntry.DecrFileName); if (decrEntry != null && decrEntry.RelativePath != fileSystemEntry.DecrFileName) { //todo: throw more specific exception return(SyncResults.Failure(new HelixException($"Case only conflict file \"{decrPath}\" exists as \"{exactPath}\"."))); } if (WhatIf) { if (entry.EncrHeader.EntryType == FileEntryType.File) { DecrDirectory.WhatIfReplaceFile(decrPath, entry.EncrHeader.Length, entry.EncrHeader.LastWriteTimeUtc); } else if (entry.EncrHeader.EntryType == FileEntryType.Removed) { DecrDirectory.WhatIfDeleteFile(decrPath); } else if (entry.EncrHeader.EntryType == FileEntryType.Directory) { DecrDirectory.WhatIfAddDirectory(decrPath); } else { throw new NotSupportedException(); } } else { HelixFile.Decrypt(encrPath, decrPath, DerivedBytesProvider); //todo: get the date on the file system (needed if the filesystem has less precision DecrDirectory.RefreshEntry(decrPath); } SyncLog.Add(fileSystemEntry); return(SyncResults.Success()); } } else if (entry.SyncMode == PreSyncMode.Match) { //Add to Log file (changed to be equal on both sides) SyncLogEntry fileSystemEntry = CreateNewLogEntryFromDecrPath(entry.DecrFileName); SyncLog.Add(fileSystemEntry); return(SyncResults.Success()); } else if (entry.SyncMode == PreSyncMode.Unchanged) { //do nothing return(SyncResults.Success()); } return(SyncResults.Failure(new HelixException($"Invalid sync mode {entry.SyncMode}"))); }
public override string ToString() { StringBuilder builder = new StringBuilder(); if (DisplayEntryType == FileEntryType.Directory) { builder.Append("<DIR> "); } else if (DisplayEntryType == FileEntryType.Purged) { builder.Append("<PUR> "); } else { builder.Append(HelixUtil.FormatBytes5(DisplayFileLength) + " "); //ex 1.5KB } if (DisplayOperation == PreSyncOperation.Add) { builder.Append("+ "); } else if (DisplayOperation == PreSyncOperation.Remove) { builder.Append("- "); } else if (DisplayOperation == PreSyncOperation.Change) { builder.Append("c "); } else if (DisplayOperation == PreSyncOperation.Purge) { builder.Append("~ "); } else if (DisplayOperation == PreSyncOperation.Error) { builder.Append("! "); } else { builder.Append(" "); } if (SyncMode == PreSyncMode.Match || SyncMode == PreSyncMode.Unchanged) { builder.Append(" "); } else if (SyncMode == PreSyncMode.DecryptedSide) { builder.Append("DEC=>ENC "); } else if (SyncMode == PreSyncMode.EncryptedSide) { builder.Append("ENC=>DEC "); } else if (SyncMode == PreSyncMode.Conflict) { builder.Append("CONFLICT "); } else //(SyncMode == PreSyncMode.Unknown) { builder.Append("UNKNOWN "); } builder.Append((EncrFileName ?? "------").Substring(0, 6) + "... "); builder.Append(DecrFileName ?? ""); return(builder.ToString()); }
public static SyncSummary Sync(SyncOptions options, ConsoleEx consoleEx = null, HelixFileVersion fileVersion = null) { var sum = new SyncSummary(); consoleEx ??= new ConsoleEx(); consoleEx.Verbosity = options.Verbosity; consoleEx.WriteLine("Sync"); if (options.WhatIf) { consoleEx.WriteLine("..Options: WhatIf"); } consoleEx.WriteLine($"..DecrDir: {options.DecrDirectory}"); consoleEx.WriteLine($"..EncrDir: {options.EncrDirectory}"); //consoleEx.WriteLine($"..Direction: {options.Direction}"); consoleEx.WriteLine($"..Verbosity: {options.Verbosity}"); consoleEx.WriteLine(); if (options.WhatIf) { consoleEx.WriteLine("** WhatIf Mode - No Changes Made **"); consoleEx.WriteLine(""); } DerivedBytesProvider derivedBytesProvider = DerivedBytesProvider.FromPassword(options.Password, options.KeyFile); using DirectoryPair pair = new DirectoryPair(options.DecrDirectory, options.EncrDirectory, derivedBytesProvider, options.WhatIf); pair.PreInitializationCheck(); if (pair.InitializeFullNeeded()) { if (options.Initialize) { //continue, unprompted if (pair.InitializeMergeWarning()) { consoleEx.WriteLine("WARNING: Decrypted directory is not empty and will be merged"); } } else { consoleEx.WriteLine("Directories require initialization..."); if (!consoleEx.PromptBool("Initialized encrypted and decrypted directories now? [y/N] ", false)) { consoleEx.WriteErrorLine("Operation cancelled"); sum.Error = new InitializationCanceledException(); return(sum); } if (pair.InitializeMergeWarning()) { if (!consoleEx.PromptBool("Decrypted directory is not empty and will be merged, continue? [y/N] ", false)) { consoleEx.WriteErrorLine("Operation cancelled"); sum.Error = new InitializationCanceledException(); return(sum); } } } pair.InitializeFull(consoleEx); } pair.OpenEncr(consoleEx); if (pair.InitializeDecrNeeded()) { if (options.Initialize) { //continue, unprompted if (pair.InitializeMergeWarning()) { consoleEx.WriteLine("WARNING: Decrypted directory is not empty and will be merged"); } } else { consoleEx.WriteLine("Decrypted directory require initialization..."); if (!consoleEx.PromptBool("Initialized decrypted directories now? [y/N] ", false)) { consoleEx.WriteErrorLine("Operation cancelled"); sum.Error = new InitializationCanceledException(); return(sum); } if (pair.InitializeMergeWarning()) { if (!consoleEx.PromptBool("Decrypted directory is not empty and will be merged, continue? [y/N] ", false)) { consoleEx.WriteErrorLine("Operation cancelled"); sum.Error = new InitializationCanceledException(); return(sum); } } } pair.InitializeDecr(consoleEx); } pair.OpenDecr(consoleEx); pair.Cleanup(consoleEx); List <PreSyncDetails> changes = pair.FindChanges(clearCache: false, console: consoleEx); if (changes.Count == 0) { consoleEx.WriteLine("--No Changes--"); } var defaultConflictAction = ""; consoleEx.WriteLine(VerbosityLevel.Normal, 0, "Performing Sync..."); foreach (PreSyncDetails change in changes) { consoleEx.WriteLine(change); if (change.SyncMode == PreSyncMode.Conflict) { var decrModified = change.DecrInfo == null ? (object)null : change.DecrInfo.LastWriteTimeUtc.ToLocalTime(); var decrSize = change.DecrInfo == null ? (object)null : HelixUtil.FormatBytes5(change.DecrInfo.Length); var encrModified = change.EncrInfo == null ? (object)null : change.EncrInfo.LastWriteTimeUtc.ToLocalTime(); var encrSize = change.EncrHeader == null ? (object)null : HelixUtil.FormatBytes5(change.EncrHeader.Length); string response; if (defaultConflictAction != "") { response = defaultConflictAction; } else { consoleEx.WriteLine($" Decrypted - Modified: {decrModified}, Size: {decrSize}"); consoleEx.WriteLine($" Encrypted - Modified: {encrModified}, Size: {encrSize}"); consoleEx.WriteLine($""); consoleEx.WriteLine($" D - Decrypted, E - Encrypted, S - Skip, + Always perform this action"); //todo: support newer, support always response = consoleEx.PromptChoice(" Select Option [D,E,S,D+,E+,S+]? ", new string[] { "D", "E", "S", "D+", "E+", "S+" }, "S"); if (response.EndsWith("+")) { response = response.Substring(0, 1); defaultConflictAction = response; } } if (response == "D") { change.SyncMode = PreSyncMode.DecryptedSide; } else if (response == "E") { change.SyncMode = PreSyncMode.EncryptedSide; } if (change.SyncMode != PreSyncMode.Conflict) { consoleEx.WriteLine(change); } } if (change.SyncMode == PreSyncMode.EncryptedSide) { //todo: make display operation be the actual operation if (change.DisplayOperation == PreSyncOperation.Add) { sum.EncrAdd++; } else if (change.DisplayOperation == PreSyncOperation.Remove) { sum.EncrRemove++; } else if (change.DisplayOperation == PreSyncOperation.Change) { sum.EncrChange++; } else { sum.EncrOther++; } } else if (change.SyncMode == PreSyncMode.DecryptedSide) { if (change.DisplayOperation == PreSyncOperation.Add) { sum.DecrAdd++; } else if (change.DisplayOperation == PreSyncOperation.Remove) { sum.DecrRemove++; } else if (change.DisplayOperation == PreSyncOperation.Change) { sum.DecrChange++; } else { sum.DecrOther++; } } else if (change.SyncMode == PreSyncMode.Conflict) { sum.Conflict++; } var syncResult = pair.TrySync(change, consoleEx); //todo: add to error log if (syncResult.Exception != null) { consoleEx.WriteErrorLine("..." + syncResult.Exception.Message); } } consoleEx.WriteLine("== Summary =="); consoleEx.WriteLine(sum); //todo: fix unchanged consoleEx.WriteLine(); //consoleEx.WriteLine(VerbosityLevel.Diagnostic, 0, ""); //consoleEx.WriteLine(VerbosityLevel.Diagnostic, 0, "==Decr Directory=="); //foreach (var entry in decrDirectory.FSDirectory.GetEntries(SearchOption.AllDirectories)) //{ // if (entry is FSDirectory dirEntry) // consoleEx.WriteLine(VerbosityLevel.Diagnostic, 1, $"<dir> {entry.RelativePath}"); // else if (entry is FSFile fileEntry) // consoleEx.WriteLine(VerbosityLevel.Diagnostic, 1, $"{HelixUtil.FormatBytes5(entry.Length)} {entry.RelativePath}"); //} //consoleEx.WriteLine(VerbosityLevel.Diagnostic, 0, ""); //consoleEx.WriteLine(VerbosityLevel.Diagnostic, 0, "==Encr Directory=="); //foreach (var entry in encrDirectory.FSDirectory.GetEntries(SearchOption.AllDirectories)) //{ // if (entry is FSDirectory dirEntry) // consoleEx.WriteLine(VerbosityLevel.Diagnostic, 1, $"<dir> {entry.RelativePath}"); // else if (entry is FSFile fileEntry) // consoleEx.WriteLine(VerbosityLevel.Diagnostic, 1, $"{HelixUtil.FormatBytes5(entry.Length)} {entry.RelativePath}"); //} return(sum); }