Esempio n. 1
0
        private async Task <bool> SendFiles(NetworkStream networkStream, List <SyncFileInfo> dataToUpload, SyncDatabase syncDb)
        {
            foreach (var fileInfo in dataToUpload)
            {
                var filePath = Path.Combine(_baseDir, fileInfo.RelativePath);

                var fileLength = new FileInfo(filePath).Length;

                var data = new SendFileCommandData
                {
                    FileLength       = fileLength,
                    SessionId        = _sessionId,
                    RelativeFilePath = fileInfo.RelativePath,
                    HashStr          = syncDb.Files.First(x => x.RelativePath == fileInfo.RelativePath).HashStr,
                };
                var dataBytes = Serializer.Serialize(data);
                await NetworkHelperSequential.WriteCommandHeader(networkStream, Commands.SendFileCmd, dataBytes.Length);

                await NetworkHelperSequential.WriteBytes(networkStream, dataBytes);

                await NetworkHelperSequential.WriteFromFileAndHashAsync(networkStream, filePath, (int)fileLength);
            }

            return(true);
        }
Esempio n. 2
0
        private async Task ProcessGetFileCmd(CommandHeader cmdHeader)
        {
            var data = await NetworkHelperSequential.Read <GetFileCommandData>(_networkStream, cmdHeader.PayloadLength);

            var ret = new ServerResponse();

            var session = SessionStorage.Instance.GetSession(data.SessionId);

            if (session == null)
            {
                ret.ErrorMsg = "Session does not exist";
            }
            else if (session.Expired)
            {
                ret.ErrorMsg = "Session has expired";
                //Log?.Invoke("Session has expired");
                //return ret;
            }
            else
            {
                data.RelativeFilePath = PathHelpers.NormalizeRelative(data.RelativeFilePath);

                Msg?.Invoke($"Sending '{data.RelativeFilePath}'");

                var filePath        = Path.Combine(session.BaseDir, data.RelativeFilePath);
                var fileLength      = new FileInfo(filePath).Length;
                var fileLengthBytes = BitConverter.GetBytes(fileLength);
                await NetworkHelperSequential.WriteCommandHeader(_networkStream, Commands.GetFileCmd, sizeof(long));

                await NetworkHelperSequential.WriteBytes(_networkStream, fileLengthBytes);

                await NetworkHelperSequential.WriteFromFileAndHashAsync(_networkStream, filePath, (int)fileLength);
            }
        }
Esempio n. 3
0
        private async Task ProcessFinishSessionCmd(CommandHeader cmdHeader)
        {
            var sessionId = await NetworkHelperSequential.Read <Guid>(_networkStream, cmdHeader.PayloadLength);

            var response = new ServerResponseWithData <SyncInfo>();

            var session = SessionStorage.Instance.GetSession(sessionId);

            if (session == null)
            {
                response.ErrorMsg = "Session does not exist";
            }
            else if (session.Expired)
            {
                response.ErrorMsg = "Session has expired";
                //Log?.Invoke("Session has expired");
                //return ret;
            }
            else
            {
                try
                {
                    FinishSession(session);
                }
                catch (Exception e)
                {
                    response.ErrorMsg = e.ToString();
                }

                //session.SyncDb = SyncDatabase.Initialize(session.BaseDir, session.SyncDbDir);
                session.SyncDb.Store(session.SyncDbDir);
            }

            await CommandHelper.WriteCommandResponse(_networkStream, Commands.FinishSessionCmd, response);
        }
Esempio n. 4
0
        public static SyncDatabase Initialize(string baseDir, string syncDbDir)
        {
            var localFiles = Directory.GetFiles(baseDir, "*", SearchOption.AllDirectories);
            var inside     = syncDbDir.StartsWith(baseDir);
            var localInfos = localFiles.Select(i =>
            {
                if (inside && i.StartsWith(syncDbDir))
                {
                    return(null);
                }

                {
                    var hash = NetworkHelperSequential.HashFileAsync(new FileInfo(i)).Result;

                    return(new SyncFileInfo
                    {
                        HashStr = hash.ToHashString(),
                        RelativePath = i.Replace(baseDir, string.Empty).TrimStart(Path.DirectorySeparatorChar),
                        AbsolutePath = i,
                        State = SyncFileState.New,
                    });
                }
            }).Where(i => i != null).ToList();

            return(new SyncDatabase
            {
                Files = localInfos,
            });
        }
Esempio n. 5
0
        private async Task <ServerResponse> FinishSession(Stream networkStream, Guid sessionId)
        {
            var cmdDataBytes = Serializer.Serialize(sessionId);

            await NetworkHelperSequential.WriteCommandHeader(networkStream, Commands.FinishSessionCmd, cmdDataBytes.Length);

            await NetworkHelperSequential.WriteBytes(networkStream, cmdDataBytes);

            var cmdHeader = await NetworkHelperSequential.ReadCommandHeader(networkStream);

            if (cmdHeader.Command != Commands.FinishSessionCmd)
            {
                return new ServerResponseWithData <SyncInfo> {
                           ErrorMsg = "Wrong command received"
                }
            }
            ;

            if (cmdHeader.PayloadLength == 0)
            {
                return new ServerResponseWithData <SyncInfo> {
                           ErrorMsg = "No data received"
                }
            }
            ;

            var responseBytes = await NetworkHelperSequential.ReadBytes(networkStream, cmdHeader.PayloadLength);

            var response = Serializer.Deserialize <ServerResponse>(responseBytes);

            return(response);
        }
Esempio n. 6
0
        private async Task <ServerResponseWithData <Guid> > GetSession(Stream networkStream)
        {
            await NetworkHelperSequential.WriteCommandHeader(networkStream, Commands.GetSessionCmd);

            var cmdHeader = await NetworkHelperSequential.ReadCommandHeader(networkStream);

            if (cmdHeader.Command != Commands.GetSessionCmd)
            {
                return(new ServerResponseWithData <Guid> {
                    ErrorMsg = "Wrong command received"
                });
            }

            if (cmdHeader.PayloadLength == 0)
            {
                return(new ServerResponseWithData <Guid> {
                    ErrorMsg = "No data received"
                });
            }

            var responseBytes = await NetworkHelperSequential.ReadBytes(networkStream, cmdHeader.PayloadLength);

            var response = Serializer.Deserialize <ServerResponseWithData <Guid> >(responseBytes);

            return(response);
        }
Esempio n. 7
0
        private async Task ProcessCommands()
        {
            try
            {
                var cmdHeader = await NetworkHelperSequential.ReadCommandHeader(_networkStream);

                switch (cmdHeader.Command)
                {
                case Commands.GetSessionCmd:
                    await ProcessGetSessionCmd();

                    break;

                case Commands.GetSyncListCmd:
                    await ProcessGetSyncListCmd(cmdHeader);

                    break;

                case Commands.GetFileCmd:
                    await ProcessGetFileCmd(cmdHeader);

                    break;

                case Commands.SendFileCmd:
                    await ProcessSendFileCmd(cmdHeader);

                    break;

                case Commands.FinishSessionCmd:
                    await ProcessFinishSessionCmd(cmdHeader);

                    break;

                case Commands.DisconnectCmd:
                    _connected = false;
                    break;

                default:
                    _connected = false;
                    break;
                }
            }
            catch (Exception e)
            {
                if (_connected)
                {
                    Debugger.Break();

                    _connected = false;

                    Console.WriteLine($"Unexpected error:\r\n{e}");
                }
                else
                {
                    // client disconnected
                    Console.WriteLine("Unexpected error but client already disconnected, ignoring...");
                }
            }
        }
        public static async Task WriteCommandResponse <T>(NetworkStream stream, byte command, T data)
        {
            var responseBytes = Serializer.Serialize(data);
            var length        = responseBytes.Length;
            await NetworkHelperSequential.WriteCommandHeader(stream, command, length);

            await NetworkHelperSequential.WriteBytes(stream, responseBytes);
        }
Esempio n. 9
0
        private async Task <bool> ReceiveFiles(Stream networkStream, IEnumerable <SyncFileInfo> dataToDownload, SyncDatabase syncDb)
        {
            foreach (var fileInfo in dataToDownload)
            {
                var data = new GetFileCommandData
                {
                    SessionId        = _sessionId,
                    RelativeFilePath = fileInfo.RelativePath,
                };
                var dataBytes = Serializer.Serialize(data);

                await NetworkHelperSequential.WriteCommandHeader(networkStream, Commands.GetFileCmd, dataBytes.Length);

                await NetworkHelperSequential.WriteBytes(networkStream, dataBytes);

                var cmdHeader = await NetworkHelperSequential.ReadCommandHeader(networkStream);

                if (cmdHeader.Command != Commands.GetFileCmd)
                {
                    return(false);
                }

                if (cmdHeader.PayloadLength == 0)
                {
                    return(false);
                }

                var fileLengthBytes = await NetworkHelperSequential.ReadBytes(networkStream, cmdHeader.PayloadLength);

                var fileLength = BitConverter.ToInt64(fileLengthBytes, 0);

                var tmpFilePath = Path.Combine(_newDir, fileInfo.RelativePath);
                var newHash     = await NetworkHelperSequential.ReadToFileAndHashAsync(networkStream, tmpFilePath, (int)fileLength);

                if (!string.Equals(newHash.ToHashString(), fileInfo.HashStr, StringComparison.OrdinalIgnoreCase))
                {
                    throw new InvalidOperationException("File copy error: hash mismatch");
                }

                _sessionFileHelper.AddNew(fileInfo.RelativePath);

                var fi = syncDb.Files.FirstOrDefault(x => x.RelativePath == fileInfo.RelativePath);
                if (fi == null)
                {
                    fi = new SyncFileInfo
                    {
                        RelativePath = fileInfo.RelativePath,
                    };

                    syncDb.Files.Add(fi);
                }

                fi.HashStr = newHash.ToHashString();
                fi.State   = SyncFileState.NotChanged;
            }

            return(true);
        }
Esempio n. 10
0
        private static void CheckState(string baseDir, string syncDbDir, SyncDatabase syncDb)
        {
            var localFiles  = Directory.GetFiles(baseDir, "*", SearchOption.AllDirectories).ToList();
            var dbDirInBase = syncDbDir.StartsWith(baseDir);

            foreach (var stored in syncDb.Files)
            {
                var localFilePath = Path.Combine(baseDir, stored.RelativePath);
                var localFileIdx  = localFiles.IndexOf(localFilePath);
                if (localFileIdx < 0)
                {
                    stored.State = SyncFileState.Deleted;
                }
                else
                {
                    var localFile = localFiles[localFileIdx];
                    localFiles.RemoveAt(localFileIdx);

                    {
                        var hash = NetworkHelperSequential.HashFileAsync(new FileInfo(localFile)).Result;

                        var localFileHash = hash.ToHashString();
                        if (localFileHash != stored.HashStr)
                        {
                            stored.State   = SyncFileState.Modified;
                            stored.HashStr = localFileHash;
                        }
                    }
                }
            }

            var localInfos = localFiles.Select(localFile =>
            {
                if (dbDirInBase && localFile.StartsWith(syncDbDir))
                {
                    return(null);
                }

                var localFileRelativePath = localFile.Replace(baseDir, string.Empty);

                {
                    var hash = NetworkHelperSequential.HashFileAsync(new FileInfo(localFile)).Result;

                    return(new SyncFileInfo
                    {
                        HashStr = hash.ToHashString(),
                        RelativePath = localFileRelativePath.TrimStart(Path.DirectorySeparatorChar),
                        AbsolutePath = localFile,
                        State = SyncFileState.New,
                    });
                }
            }).Where(i => i != null).ToList();

            syncDb.Files.AddRange(localInfos);
        }
Esempio n. 11
0
        private async Task ProcessSendFileCmd(CommandHeader cmdHeader)
        {
            var data = await NetworkHelperSequential.Read <SendFileCommandData>(_networkStream, cmdHeader.PayloadLength);

            var ret = new ServerResponse();

            var session = SessionStorage.Instance.GetSession(data.SessionId);

            if (session == null)
            {
                ret.ErrorMsg = "Session does not exist";
            }
            else if (session.Expired)
            {
                ret.ErrorMsg = "Session has expired";
                Msg?.Invoke("Session has expired");
            }
            else
            {
                data.RelativeFilePath = PathHelpers.NormalizeRelative(data.RelativeFilePath);

                var filePath = Path.Combine(session.NewDir, data.RelativeFilePath);

                var fileDir = Path.GetDirectoryName(filePath);
                PathHelpers.EnsureDirExists(fileDir);

                Msg?.Invoke($"Receiving file '{data.RelativeFilePath}'");

                var newHash = await NetworkHelperSequential.ReadToFileAndHashAsync(_networkStream, filePath, data.FileLength);

                var fileInfo = session.SyncDb.Files.FirstOrDefault(i => i.RelativePath == data.RelativeFilePath);
                if (fileInfo != null)
                {
                    fileInfo.HashStr = newHash.ToHashString();
                    fileInfo.State   = SyncFileState.NotChanged;
                }
                else
                {
                    session.SyncDb.AddFile(session.BaseDir, data.RelativeFilePath, newHash.ToHashString());
                }
            }
        }
Esempio n. 12
0
        private async Task <ServerResponseWithData <SyncInfo> > GetSyncList(Stream networkStream, Guid sessionId, List <SyncFileInfo> syncDbFiles)
        {
            var cmdData = new GetSyncListCommandData
            {
                SessionId = sessionId,
                Files     = syncDbFiles,
            };

            var cmdDataBytes = Serializer.Serialize(cmdData);

            await NetworkHelperSequential.WriteCommandHeader(networkStream, Commands.GetSyncListCmd, cmdDataBytes.Length);

            await NetworkHelperSequential.WriteBytes(networkStream, cmdDataBytes);

            var cmdHeader = await NetworkHelperSequential.ReadCommandHeader(networkStream);

            if (cmdHeader.Command != Commands.GetSyncListCmd)
            {
                return new ServerResponseWithData <SyncInfo> {
                           ErrorMsg = "Wrong command received"
                }
            }
            ;

            if (cmdHeader.PayloadLength == 0)
            {
                return new ServerResponseWithData <SyncInfo> {
                           ErrorMsg = "No data received"
                }
            }
            ;

            var responseBytes = await NetworkHelperSequential.ReadBytes(networkStream, cmdHeader.PayloadLength);

            var response = Serializer.Deserialize <ServerResponseWithData <SyncInfo> >(responseBytes);

            return(response);
        }
Esempio n. 13
0
        private async Task ProcessGetSyncListCmd(CommandHeader cmdHeader)
        {
            var data = await NetworkHelperSequential.Read <GetSyncListCommandData>(_networkStream, cmdHeader.PayloadLength);

            var ret = new ServerResponseWithData <SyncInfo>();

            var session = SessionStorage.Instance.GetSession(data.SessionId);

            if (session == null)
            {
                ret.ErrorMsg = "Session does not exist";
            }
            else if (session.Expired)
            {
                ret.ErrorMsg = "Session has expired";
                //Log?.Invoke("Session has expired");
                //return ret;
            }
            else
            {
                Msg?.Invoke("Scanning local folder...");

                var syncDb = GetSyncDb(session.BaseDir, session.SyncDbDir, out var error);
                if (syncDb == null)
                {
                    ret.ErrorMsg = error;
                    Msg?.Invoke($"Failed to get sync db: {error}");
                }
                else
                {
                    var syncInfo = new SyncInfo();

                    PathHelpers.NormalizeRelative(data.Files);

                    Msg?.Invoke("Preparing sync list...");

                    foreach (var localFileInfo in syncDb.Files)
                    {
                        var remoteFileInfo = data.Files.FirstOrDefault(remoteFile =>
                                                                       remoteFile.RelativePath == localFileInfo.RelativePath);
                        if (remoteFileInfo == null)
                        {
                            if (localFileInfo.State != SyncFileState.Deleted)
                            {
                                syncInfo.ToDownload.Add(localFileInfo);
                            }
                        }
                        else
                        {
                            data.Files.Remove(remoteFileInfo);

                            switch (remoteFileInfo.State)
                            {
                            case SyncFileState.Deleted:
                                if (localFileInfo.State == SyncFileState.NotChanged ||
                                    localFileInfo.State == SyncFileState.Deleted)
                                {
                                    var filePath = Path.Combine(session.BaseDir, localFileInfo.RelativePath);
                                    if (File.Exists(filePath))
                                    {
                                        var movedFilePath = Path.Combine(session.RemovedDir, localFileInfo.RelativePath);
                                        var movedFileDir  = Path.GetDirectoryName(movedFilePath);
                                        if (movedFileDir == null)
                                        {
                                            throw new InvalidOperationException($"Unable to get '{movedFilePath}'s dir");
                                        }
                                        if (!Directory.Exists(movedFileDir))
                                        {
                                            Directory.CreateDirectory(movedFileDir);
                                        }

                                        File.Move(filePath, movedFilePath);
                                    }
                                }
                                else if (localFileInfo.State == SyncFileState.New)
                                {
                                    syncInfo.ToDownload.Add(localFileInfo);
                                }
                                else
                                {
                                    syncInfo.Conflicts.Add(localFileInfo);
                                }

                                break;

                            case SyncFileState.New:
                                syncInfo.Conflicts.Add(localFileInfo);
                                break;

                            case SyncFileState.Modified:
                                if (localFileInfo.State == SyncFileState.NotChanged)
                                {
                                    syncInfo.ToUpload.Add(localFileInfo);
                                }
                                else
                                {
                                    syncInfo.Conflicts.Add(remoteFileInfo);
                                }
                                break;

                            case SyncFileState.NotChanged:
                                if (localFileInfo.State == SyncFileState.Modified)
                                {
                                    syncInfo.ToDownload.Add(localFileInfo);
                                }
                                else if (localFileInfo.State == SyncFileState.Deleted)
                                {
                                    syncInfo.ToRemove.Add(remoteFileInfo);
                                }
                                else if (localFileInfo.State == SyncFileState.New)
                                {
                                    Debugger.Break();     // not possible
                                }

                                break;
                            }
                        }
                    }

                    foreach (var remoteFileInfo in data.Files.Where(x => x.State != SyncFileState.Deleted))
                    {
                        syncInfo.ToUpload.Add(remoteFileInfo);
                    }

                    ret.Data = syncInfo;

                    session.SyncDb = syncDb;
                }
            }

            await CommandHelper.WriteCommandResponse(_networkStream, Commands.GetSyncListCmd, ret);
        }
Esempio n. 14
0
        public async Task Sync()
        {
            try
            {
                using (var client = new TcpClient())
                {
                    await client.ConnectAsync(IPAddress.Parse(_serverAddress), _serverPort);

                    using (var networkStream = client.GetStream())
                    {
                        var sessionId = await GetSession(networkStream);

                        if (sessionId.HasError)
                        {
                            Log?.Invoke($"Unable to create sync session. Server response was '{sessionId.ErrorMsg}'");
                            return;
                        }

                        _sessionId = sessionId.Data;

                        if (!Directory.Exists(_syncDbDir))
                        {
                            var dirInfo = Directory.CreateDirectory(_syncDbDir);
                            dirInfo.Attributes = dirInfo.Attributes | FileAttributes.Hidden;
                        }

                        var syncDb = GetLocalSyncDb(out var error);
                        if (syncDb == null)
                        {
                            Log?.Invoke(error);
                            return;
                        }

                        var syncList = await GetSyncList(networkStream, _sessionId, syncDb.Files);

                        if (syncList.HasError)
                        {
                            Log?.Invoke($"Unable to get sync list. Server response was '{syncList.ErrorMsg}'");
                            return;
                        }

                        PathHelpers.NormalizeRelative(syncList.Data.ToDownload, syncList.Data.ToUpload, syncList.Data.ToRemove);

                        PathHelpers.EnsureDirExists(_toRemoveDir);
                        PathHelpers.EnsureDirExists(_newDir);

                        foreach (var fileInfo in syncList.Data.ToRemove)
                        {
                            _sessionFileHelper.PrepareForRemove(fileInfo.RelativePath);

                            var fi = syncDb.Files.First(x => x.RelativePath == fileInfo.RelativePath);
                            syncDb.Files.Remove(fi);
                        }

                        if (syncList.Data.Conflicts.Count > 0)
                        {
                            Debugger.Break();
                        }

                        if (!await ReceiveFiles(networkStream, syncList.Data.ToDownload, syncDb))
                        {
                            return;
                        }

                        if (!await SendFiles(networkStream, syncList.Data.ToUpload, syncDb))
                        {
                            return;
                        }

                        var response = await FinishSession(networkStream, _sessionId);

                        if (response.HasError)
                        {
                            Log?.Invoke($"Error finishing session. Server response was '{response.ErrorMsg}'");

                            return;
                        }

                        _sessionFileHelper.FinishSession();

                        syncDb.Files.RemoveAll(x => x.State == SyncFileState.Deleted);
                        syncDb.Store(_syncDbDir);

                        File.WriteAllText(Path.Combine(_syncDbDir, $"sync-{DateTime.Now:dd-MM-yyyy_hh-mm-ss}.log"), _log.ToString());

                        if (new DirectoryInfo(_newDir).EnumerateFiles("*", SearchOption.AllDirectories).Any())
                        {
                            Debugger.Break(); // all files should be removed by now
                        }

                        if (new DirectoryInfo(_toRemoveDir).EnumerateFiles("*", SearchOption.AllDirectories).Any())
                        {
                            Debugger.Break(); // all files should be removed by now
                        }

                        Directory.Delete(_newDir, true);

                        Directory.Delete(_toRemoveDir, true);

                        await NetworkHelperSequential.WriteCommandHeader(networkStream, Commands.DisconnectCmd);
                    }
                }
            }
            catch (Exception e)
            {
                Log?.Invoke($"Error during sync {e}");
            }
        }