Skip to content

Commit 3bb8c81

Browse files
authored
Merge pull request #6 from Synergex/MaybeCrashFixPlus
Fix up real-time sync after it was broken in 1.5.
2 parents eb5da06 + 3e663e1 commit 3bb8c81

8 files changed

Lines changed: 192 additions & 116 deletions

File tree

SFTPSyncLib/Logger.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,11 @@ private static void Log(string message)
4040
switch (_mode)
4141
{
4242
case LoggerMode.Console:
43-
Console.WriteLine($"{DateTime.Now.ToString()} {message}");
43+
Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} {message}");
4444
break;
4545

4646
case LoggerMode.RaiseEvent:
47-
LogUpdated?.Invoke($"{DateTime.Now.ToString()} {message}");
47+
LogUpdated?.Invoke($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} {message}");
4848
break;
4949
}
5050
}

SFTPSyncLib/RemoteSync.cs

Lines changed: 50 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -33,25 +33,25 @@ public RemoteSync(string host, string username, string password,
3333
_username = username;
3434
_password = password;
3535
_searchPattern = searchPattern;
36-
_localRootDirectory = localRootDirectory;
37-
_remoteRootDirectory = remoteRootDirectory;
36+
_localRootDirectory = Path.TrimEndingDirectorySeparator(Path.GetFullPath(localRootDirectory));
37+
_remoteRootDirectory = remoteRootDirectory.TrimEnd('/', '\\');
3838
_director = director;
3939
_excludedFolders = excludedFolders ?? new List<string>();
4040
_sftp = new SftpClient(host, username, password);
41-
_sftp.Connect();
4241

43-
//Our first instance is responsible for creating all of the the directories
44-
//Subsequent instances will not be created until this is done
45-
DoneMakingFolders = createFolders ? CreateDirectories(_localRootDirectory, _remoteRootDirectory) : Task.CompletedTask;
42+
//The first instance is responsible for creating ALL of the the directories.
43+
//Subsequent instances will not be created until this one completes.
44+
45+
DoneMakingFolders = createFolders
46+
? CreateDirectories(_localRootDirectory, _remoteRootDirectory)
47+
: Task.CompletedTask;
4648

4749
//Now perform the initial sync for the pattern this instance is responsible for
50+
4851
DoneInitialSync = InitialSync(_localRootDirectory, _remoteRootDirectory);
4952

50-
//Once the initial sync is done, we can start watching the file system for changes
51-
DoneInitialSync.ContinueWith((tmp) =>
52-
{
53-
_director.AddCallback(searchPattern, (args) => Fsw_Changed(null, args));
54-
});
53+
// Register callbacks immediately; handler will ignore events until initial sync completes.
54+
_director.AddCallback(searchPattern, (args) => Fsw_Changed(null, args));
5555
}
5656

5757
public RemoteSync(string host, string username, string password,
@@ -62,31 +62,24 @@ public RemoteSync(string host, string username, string password,
6262
_username = username;
6363
_password = password;
6464
_searchPattern = searchPattern;
65-
_localRootDirectory = localRootDirectory;
66-
_remoteRootDirectory = remoteRootDirectory;
65+
_localRootDirectory = Path.TrimEndingDirectorySeparator(Path.GetFullPath(localRootDirectory));
66+
_remoteRootDirectory = remoteRootDirectory.TrimEnd('/', '\\');
6767
_director = director;
6868
_excludedFolders = excludedFolders ?? new List<string>();
6969
_sftp = new SftpClient(host, username, password);
70-
_sftp.Connect();
7170

7271
DoneMakingFolders = Task.CompletedTask;
72+
7373
DoneInitialSync = initialSyncTask;
7474

75-
DoneInitialSync.ContinueWith((tmp) =>
76-
{
77-
_director.AddCallback(searchPattern, (args) => Fsw_Changed(null, args));
78-
});
75+
// Register callbacks immediately; handler will ignore events until initial sync completes.
76+
_director.AddCallback(searchPattern, (args) => Fsw_Changed(null, args));
7977
}
8078

8179
public static async Task RunSharedInitialSyncAsync(
82-
string host,
83-
string username,
84-
string password,
85-
string localRootDirectory,
86-
string remoteRootDirectory,
87-
string[] searchPatterns,
88-
List<string>? excludedFolders,
89-
int workerCount)
80+
string host, string username, string password,
81+
string localRootDirectory, string remoteRootDirectory,
82+
string[] searchPatterns, List<string>? excludedFolders, int workerCount)
9083
{
9184
if (workerCount <= 0 || searchPatterns.Length == 0)
9285
{
@@ -108,6 +101,7 @@ public static async Task RunSharedInitialSyncAsync(
108101
}
109102

110103
var workQueue = new ConcurrentQueue<SyncWorkItem>();
104+
111105
foreach (var pair in EnumerateLocalDirectories(localRootDirectory, remoteRootDirectory, excludedFolders))
112106
{
113107
foreach (var pattern in searchPatterns)
@@ -117,6 +111,7 @@ public static async Task RunSharedInitialSyncAsync(
117111
}
118112

119113
var workers = new List<Task>();
114+
120115
for (int i = 0; i < workerCount; i++)
121116
{
122117
workers.Add(Task.Run(async () =>
@@ -150,7 +145,7 @@ public static async Task RunSharedInitialSyncAsync(
150145
/// <param name="destinationPath">Path to the destination file</param>
151146
public static void SyncFile(SftpClient sftp, string sourcePath, string destinationPath)
152147
{
153-
Logger.LogInfo($"Syncing {sourcePath} -> {destinationPath}");
148+
Logger.LogInfo($"Syncing {sourcePath}");
154149

155150
int retryCount = 0;
156151
const int maxRetries = 5;
@@ -195,7 +190,7 @@ public static Task<IEnumerable<FileInfo>> SyncDirectoryAsync(SftpClient sftp, st
195190
{
196191
if (new DirectoryInfo(sourcePath).EnumerateFiles(searchPattern, SearchOption.TopDirectoryOnly).Any())
197192
{
198-
Logger.LogInfo($"Sync started for {sourcePath}\\{searchPattern} -> {destinationPath}");
193+
Logger.LogInfo($"Sync started for {sourcePath}\\{searchPattern}");
199194

200195
return Task<IEnumerable<FileInfo>>.Factory.FromAsync(sftp.BeginSynchronizeDirectories,
201196
sftp.EndSynchronizeDirectories, sourcePath,
@@ -214,7 +209,7 @@ private string[] FilteredDirectories(string localPath)
214209

215210
public async Task CreateDirectories(string localPath, string remotePath)
216211
{
217-
Logger.LogInfo($"Creating directory {localPath} -> {remotePath}");
212+
Logger.LogInfo($"Creating directory {remotePath}");
218213

219214
try
220215
{
@@ -337,6 +332,7 @@ private static async Task CreateDirectoriesInternal(
337332
var directoryName = item.Split(Path.DirectorySeparatorChar).Last();
338333
if (!remoteDirectories.ContainsKey(directoryName))
339334
{
335+
Logger.LogInfo($"Creating remote directory {remotePath}{directoryName}");
340336
sftp.CreateDirectory(remotePath + "/" + directoryName);
341337
}
342338
await CreateDirectoriesInternal(sftp, localRootDirectory, localPath + "\\" + directoryName, remotePath + "/" + directoryName, excludedFolders);
@@ -383,6 +379,19 @@ public static bool IsFileReady(String sFilename)
383379
}
384380
}
385381

382+
private string GetRemotePathForLocal(string localPath)
383+
{
384+
var relativePath = Path.GetRelativePath(_localRootDirectory, localPath);
385+
if (relativePath == "." || string.IsNullOrEmpty(relativePath))
386+
return _remoteRootDirectory;
387+
388+
relativePath = relativePath.Replace('\\', '/').TrimStart('/');
389+
if (relativePath.Length == 0)
390+
return _remoteRootDirectory;
391+
392+
return _remoteRootDirectory + "/" + relativePath;
393+
}
394+
386395
private void EnsureConnected()
387396
{
388397
if (_disposed)
@@ -408,17 +417,25 @@ private bool EnsureConnectedSafe()
408417
}
409418
}
410419

420+
public Task<bool> ConnectAsync()
421+
{
422+
return Task.Run(() => EnsureConnectedSafe());
423+
}
424+
411425

412426
private async void Fsw_Changed(object? sender, FileSystemEventArgs arg)
413427
{
414428
try
415429
{
430+
if (!DoneInitialSync.IsCompleted)
431+
return;
432+
416433
if (arg.ChangeType == WatcherChangeTypes.Changed || arg.ChangeType == WatcherChangeTypes.Created
417434
|| arg.ChangeType == WatcherChangeTypes.Renamed)
418435
{
419436
var changedPath = Path.GetDirectoryName(arg.FullPath);
420-
var relativePath = _localRootDirectory == changedPath ? "" : changedPath?.Substring(_localRootDirectory.Length).Replace('\\', '/');
421-
var fullRemotePath = _remoteRootDirectory + relativePath;
437+
var fullRemotePath = GetRemotePathForLocal(changedPath ?? _localRootDirectory);
438+
var fullRemoteFilePath = GetRemotePathForLocal(arg.FullPath);
422439
await Task.Yield();
423440
bool makeDirectory = true;
424441
lock (_activeDirSync)
@@ -439,7 +456,7 @@ private async void Fsw_Changed(object? sender, FileSystemEventArgs arg)
439456
if (connectionOk)
440457
{
441458
//check if we're a new directory
442-
if (makeDirectory && Directory.Exists(arg.FullPath) && !_sftp.Exists(fullRemotePath))
459+
if (makeDirectory && changedPath != null && Directory.Exists(changedPath) && !_sftp.Exists(fullRemotePath))
443460
{
444461
_sftp.CreateDirectory(fullRemotePath);
445462
}
@@ -501,7 +518,7 @@ private async void Fsw_Changed(object? sender, FileSystemEventArgs arg)
501518
fileConnectionOk = EnsureConnectedSafe();
502519
if (fileConnectionOk)
503520
{
504-
SyncFile(_sftp, arg.FullPath, fullRemotePath + "/" + Path.GetFileName(arg.FullPath));
521+
SyncFile(_sftp, arg.FullPath, fullRemoteFilePath);
505522
}
506523
}
507524
finally

SFTPSyncLib/SyncDirector.cs

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,47 +18,52 @@ public SyncDirector(string rootFolder)
1818
InternalBufferSize = 64 * 1024
1919
};
2020

21-
_fsw.Changed += Fsw_Changed;
2221
_fsw.Created += Fsw_Created;
22+
_fsw.Changed += Fsw_Changed;
2323
_fsw.Renamed += Fsw_Renamed;
2424
_fsw.Error += Fsw_Error;
2525

2626
_fsw.EnableRaisingEvents = true;
2727
}
2828

29-
private void Fsw_Renamed(object sender, RenamedEventArgs e)
29+
public void AddCallback(string match, Action<FileSystemEventArgs> handler)
30+
{
31+
string regexPattern = "^" + Regex.Escape(match)
32+
.Replace("\\*", ".*")
33+
.Replace("\\?", ".") + "$";
34+
callbacks.Add((new Regex(regexPattern, RegexOptions.IgnoreCase), handler));
35+
}
36+
37+
private void Fsw_Created(object sender, FileSystemEventArgs e)
3038
{
39+
var name = Path.GetFileName(e.FullPath);
3140
foreach (var (regex, callback) in callbacks)
3241
{
33-
if (regex.IsMatch(e.FullPath))
42+
if (regex.IsMatch(name))
3443
{
3544
callback(e);
3645
}
3746
}
3847
}
3948

40-
private void Fsw_Created(object sender, FileSystemEventArgs e)
49+
private void Fsw_Changed(object sender, FileSystemEventArgs e)
4150
{
51+
var name = Path.GetFileName(e.FullPath);
4252
foreach (var (regex, callback) in callbacks)
4353
{
44-
if (regex.IsMatch(e.FullPath))
54+
if (regex.IsMatch(name))
4555
{
4656
callback(e);
4757
}
4858
}
4959
}
5060

51-
public void AddCallback(string match, Action<FileSystemEventArgs> handler)
52-
{
53-
string regexPattern = "^" + Regex.Escape(match).Replace("\\*", ".*") + "$";
54-
callbacks.Add((new Regex(regexPattern), handler));
55-
}
56-
57-
private void Fsw_Changed(object sender, FileSystemEventArgs e)
61+
private void Fsw_Renamed(object sender, RenamedEventArgs e)
5862
{
63+
var name = Path.GetFileName(e.FullPath);
5964
foreach (var (regex, callback) in callbacks)
6065
{
61-
if (regex.IsMatch(e.FullPath))
66+
if (regex.IsMatch(name))
6267
{
6368
callback(e);
6469
}

SFTPSyncSetup/Product.wxs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
Id="*"
66
Name="SFTPSync"
77
Language="1033"
8-
Version="1.5"
8+
Version="1.6"
99
Manufacturer="Synergex International Corporation"
1010
UpgradeCode="6000f870-b811-4e22-b80b-5b8956317d09">
1111

SFTPSyncSetup/SFTPSyncSetup.wixproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<ProductVersion>3.10</ProductVersion>
77
<ProjectGuid>5074cbcb-641b-4a9c-b3bc-8dd0b78810a6</ProjectGuid>
88
<SchemaVersion>2.0</SchemaVersion>
9-
<OutputName>SFTPSync-1.5</OutputName>
9+
<OutputName>SFTPSync-1.6</OutputName>
1010
<OutputType>Package</OutputType>
1111
<Name>SFTPSyncSetup</Name>
1212
</PropertyGroup>

SFTPSyncUI/MainForm.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,15 +159,15 @@ private void AppendLog(string message)
159159

160160
private void AddMessage(string message)
161161
{
162-
listBoxMessages.Items.Add(message);
162+
listBoxMessages.Items.Insert(0, message);
163163

164-
// Optional: auto-scroll to bottom
165-
listBoxMessages.TopIndex = listBoxMessages.Items.Count - 1;
164+
// Keep newest at top
165+
listBoxMessages.TopIndex = 0;
166166

167167
// Optional: trim excess from UI if _log did a dequeue
168168
if (listBoxMessages.Items.Count > 1000)
169169
{
170-
listBoxMessages.Items.RemoveAt(0);
170+
listBoxMessages.Items.RemoveAt(listBoxMessages.Items.Count - 1);
171171
}
172172
}
173173

0 commit comments

Comments
 (0)