private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) { //ServicePointManager.ServerCertificateValidationCallback=delegate //{ // return true; //}; RemoteCertificateValidationCallback certificateCallback=new RemoteCertificateValidationCallback(ValidateServerCertificate); string syncFolder=Globals.Options.GetElementAsString("xml.Options.Paths.SyncFolder"); string ftpServer=Globals.Options.GetElementAsString("xml.Options.FTP.Server"); string ftpUser=Globals.Options.GetElementAsString("xml.Options.FTP.User"); string ftpPassword=Globals.Options.GetElementAsString("xml.Options.FTP.Password"); string ftpProtocol=Globals.Options.GetElementAsString("xml.Options.FTP.Protocol"); if(FileSystem.ExistsDirectory(syncFolder)) { if(ftpServer!=""&&ftpUser!=""&ftpPassword!=""&ftpProtocol!="") { //Connect FTPSClient client=new FTPSClient(); //NetworkCredital NetworkCredential credential=new NetworkCredential(ftpUser, ftpPassword); //client.Connect(ftpServer, credential, ESSLSupportMode.ControlAndDataChannelsRequired); client.Connect(ftpServer, credential, ESSLSupportMode.ControlAndDataChannelsRequired, certificateCallback); #region Verzeichnisstruktur überprüfen und nach Bedarf anlegen //Test auf leeres Verzeichnis IList<DirectoryListItem> files=client.GetDirectoryList(); bool existXData=false; bool existHData=false; bool existLData=false; bool existCData=false; bool existMData=false; bool existRData=false; bool existCfsInfo=false; foreach(DirectoryListItem file in files) { if(file.IsDirectory) { switch(file.Name) { case "x-data": { existXData=true; break; } case "h-data": { existHData=true; break; } case "l-data": { existLData=true; break; } case "c-data": { existCData=true; break; } case "m-data": { existMData=true; break; } case "r-data": { existRData=true; break; } } } else //File { switch(file.Name) { case "cfsinfo": { existCfsInfo=true; break; } } } } //CfsInfo anlegen if(existCfsInfo==false) { List<string> version=new List<string>(); version.Add("1"); //Repository Version string versionFilenameLocal=FileSystem.TempPath+"cfsinfo-"+Various.GetTimeID(); File.WriteAllLines(versionFilenameLocal, version.ToArray()); client.PutFile(versionFilenameLocal, "cfsinfo"); } else //Version überprüfen { string versionFilenameLocal=FileSystem.TempPath+"cfsinfo-"+Various.GetTimeID(); client.GetFile("cfsinfo", versionFilenameLocal); string[] cfsinfo=File.ReadAllLines(versionFilenameLocal); int version=Convert.ToInt32(cfsinfo[0]); if(version!=1) { MessageBox.Show("CloudFileSync ist nicht mit der Version des Repositories kompatibel!", "Hinweis", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } } if(existXData==false) client.MakeDir("x-data"); //Datenordner if(existHData==false) client.MakeDir("h-data"); //Hashdaten if(existLData==false) client.MakeDir("l-data"); //Lockdaten if(existCData==false) client.MakeDir("c-data"); //Createdaten if(existMData==false) client.MakeDir("m-data"); //Modifieddaten if(existRData==false) client.MakeDir("r-data"); //Removedaten //Überprüfen ob meine Clientverzeichnisse existieren CheckAndCreateClientFolder(client, "c-data"); CheckAndCreateClientFolder(client, "m-data"); CheckAndCreateClientFolder(client, "r-data"); #endregion //Init Datenbestand (Sync) UpdateFromRemote(client, syncFolder); //Neu erzeugte oder geänderte Dateien ermitteln List<string> syncFolderFiles=FileSystem.GetFiles(syncFolder, true); //Neue Lokale Dateien hochladen foreach(string file in syncFolderFiles) { string relFilename=FileSystem.GetRelativePath(file, syncFolder, true); string hashRemote=GetHistoryHash(client, relFilename); string hashLocal=Hash.SHA1.HashFile(file); if(hashRemote!=hashLocal) { filesCreated.Add(file); } } //gelöschte Dateien ermitteln List<string> hashFiles=GetHashFiles(client); foreach(string hashFile in hashFiles) { string filenameToTest=FileSystem.GetPathWithPathDelimiter(syncFolder)+hashFile.Replace('\\', FileSystem.PathDelimiter); syncFolderFiles.Remove(filenameToTest); } foreach(string file in syncFolderFiles) { if(filesCreated.IndexOf(file)==-1) //Datei soll nicht hochgeladen werden, sondern wurde wirklich auf einem anderen Client gelöscht { string relFilename=FileSystem.GetRelativePath(file, syncFolder, true); FileSystem.RemoveFile(file); Globals.Log.Add(LogLevel.Information, "Lokal: Datei {0} wurde gelöscht.", relFilename); } } //FilesystemWatcher aktivieren fileSystemWatcher.Path=syncFolder; fileSystemWatcher.EnableRaisingEvents=true; #region Endlosschleife für Syncronisation while(true) { if(e.Cancel==true) return; List<string> fileToRemoveFromList=new List<string>(); //Dateien hochladen #region Created files lock(filesCreated) { foreach(string fileCreated in filesCreated) { string relFilename=FileSystem.GetRelativePath(fileCreated, syncFolder, true); relFilename=relFilename.Replace("\\", "/"); bool locked=GetFileLock(client, relFilename); if(locked) { if(FileSystem.IsDirectory(fileCreated)) //Directory { client.CreateDirectory("x-data/"+relFilename); fileToRemoveFromList.Add(fileCreated); Globals.Log.Add(LogLevel.Information, "Remote: Verzeichnis {0} wurde erstellt (x-data).", relFilename); //In Created Verzeichnisse legen AddFileToUpdateFolder(client, "c-data", relFilename, EFileType.Directory); //Globals.Log.Add(LogLevel.Information, "Updatedatei {0} wurde hochgeladen (c-data).", relFilename); } else //File { //Hashdatei ändern AddHistoryHash(client, fileCreated, relFilename); Globals.Log.Add(LogLevel.Information, "Remote: Hash für {0} wurde geändert (x-data).", relFilename); //Datei hochladen CreateFTPDirectories(client, "x-data/"+relFilename); client.PutFile(fileCreated, "x-data/"+relFilename); fileToRemoveFromList.Add(fileCreated); Globals.Log.Add(LogLevel.Information, "Remote: Datei {0} wurde hochgeladen (x-data).", relFilename); //In Created Verzeichnisse legen AddFileToUpdateFolder(client, "c-data", relFilename, EFileType.File); //Globals.Log.Add(LogLevel.Information, "Updatedatei {0} wurde hochgeladen (c-data).", relFilename); } } RemoveFileLock(client, relFilename); } foreach(string rFile in fileToRemoveFromList) { filesCreated.Remove(rFile); } } fileToRemoveFromList.Clear(); #endregion #region Changed Files lock(filesChanged) { foreach(string fileChanged in filesChanged) { string relFilename=FileSystem.GetRelativePath(fileChanged, syncFolder, true); relFilename=relFilename.Replace("\\", "/"); bool locked=GetFileLock(client, relFilename); if(locked) { if(FileSystem.IsDirectory(fileChanged)) //Directory { //Sollte bei Verzeichnissen ignoriert werden } else //File { //Hashdatei ändern AddHistoryHash(client, fileChanged, relFilename); Globals.Log.Add(LogLevel.Information, "Remote: Hash für {0} wurde geändert (x-data).", relFilename); //Datei hochladen client.PutFile(fileChanged, "x-data/"+relFilename); fileToRemoveFromList.Add(fileChanged); Globals.Log.Add(LogLevel.Information, "Remote: Datei {0} wurde modifiziert (x-data).", relFilename); //In Created Verzeichnisse legen AddFileToUpdateFolder(client, "m-data", relFilename, EFileType.File); } } RemoveFileLock(client, relFilename); } foreach(string rFile in fileToRemoveFromList) { filesChanged.Remove(rFile); } } fileToRemoveFromList.Clear(); #endregion #region Deleted Files lock(filesDeleted) { foreach(string fileDeleted in filesDeleted) { string relFilename=FileSystem.GetRelativePath(fileDeleted, syncFolder, true); relFilename=relFilename.Replace("\\", "/"); bool locked=GetFileLock(client, relFilename); if(locked) { if(FileSystem.IsDirectory(fileDeleted)) //Directory { client.RemoveDir(fileDeleted); //client.PutFile(fileChanged, "x-data/"+relFilename); fileToRemoveFromList.Add(fileDeleted); Globals.Log.Add(LogLevel.Information, "Remote: Verzeichnis {0} wurde gelöscht (x-data).", relFilename); //In Created Verzeichnisse legen AddFileToUpdateFolder(client, "r-data", relFilename, EFileType.File); } else //File { //Hashdatei löchen RemoveHistoryHash(client, relFilename); Globals.Log.Add(LogLevel.Information, "Remote: Hash für {0} wurde gelöscht (x-data).", relFilename); //Datei löschen client.DeleteFile("x-data/"+relFilename); fileToRemoveFromList.Add(fileDeleted); Globals.Log.Add(LogLevel.Information, "Remote: Datei {0} wurde gelöscht (x-data).", relFilename); //In Created Verzeichnisse legen AddFileToUpdateFolder(client, "r-data", relFilename, EFileType.File); } } RemoveFileLock(client, relFilename); } foreach(string rFile in fileToRemoveFromList) { filesDeleted.Remove(rFile); } } fileToRemoveFromList.Clear(); #endregion //Neue Dateien herunterladen bzw. gelöscht Dateien löschen UpdateFromRemote(client, syncFolder); //Halbe Sekunde warten Thread.Sleep(500); } #endregion //string fileInfo=Globals.Options.GetElementAsString("xml.Sync.Filesystem.Test.txt"); //client.PutFile("D:\\AlexFTPS_bin_1.0.2.zip", "xxx.xxx"); } } }
public void UpdateFromRemote(FTPSClient client, string syncFolder) { #region Removed List<FileReturn> filesToUpdate=GetFilesFromUpdateFolder(client, "r-data"); if(filesToUpdate.Count>0) { foreach(FileReturn fileReturn in filesToUpdate) { if(fileReturn.FileType==EFileType.File) { string localFilename=syncFolder+"/"+fileReturn.Filename; FileSystem.RemoveFile(localFilename); Globals.Log.Add(LogLevel.Information, "Lokal: Datei {0} wurde gelöscht (x-data).", fileReturn.Filename); } else if(fileReturn.FileType==EFileType.Directory) { throw new NotImplementedException(); } } } #endregion #region Created filesToUpdate=GetFilesFromUpdateFolder(client, "c-data"); if(filesToUpdate.Count>0) { foreach(FileReturn fileReturn in filesToUpdate) { if(fileReturn.FileType==EFileType.File) { string localFilename=syncFolder+"/"+fileReturn.Filename; client.GetFile("x-data/"+fileReturn.Filename, localFilename); Globals.Log.Add(LogLevel.Information, "Lokal: Datei {0} wurde erzeugt (x-data).", fileReturn.Filename); } else if(fileReturn.FileType==EFileType.Directory) { string localFilename=syncFolder+"/"+fileReturn.Filename; client.GetFile("x-data/"+fileReturn.Filename, localFilename); Globals.Log.Add(LogLevel.Information, "Lokal: Verzeichnis {0} wurde erzeugt (x-data).", fileReturn.Filename); } } } #endregion #region Modified filesToUpdate=GetFilesFromUpdateFolder(client, "m-data"); if(filesToUpdate.Count>0) { foreach(FileReturn fileReturn in filesToUpdate) { if(fileReturn.FileType==EFileType.File) { string localFilename=syncFolder+"/"+fileReturn.Filename; client.GetFile("x-data/"+fileReturn.Filename, localFilename); Globals.Log.Add(LogLevel.Information, "Lokal: Datei {0} wurde modifiziert (x-data).", fileReturn.Filename); } else if(fileReturn.FileType==EFileType.Directory) { throw new NotImplementedException(); } } } #endregion }
public string GetHistoryHash(FTPSClient client, string relFilename) { string fileNameFTP=relFilename.Replace("/", "\\"); fileNameFTP="h-data/"+fileNameFTP; if(client.Exists(fileNameFTP)) //Neuen Hash hinzufügen { string hashFilenameLocal=FileSystem.TempPath+FileSystem.GetFilename(fileNameFTP); client.GetFile(fileNameFTP, hashFilenameLocal); List<string> hashInfo=new List<string>(); hashInfo.AddRange(File.ReadAllLines(hashFilenameLocal)); return hashInfo[0]; } else //keine Hashdatei vorhanden { return ""; } }
/// <summary> /// Gibt die Updatedateien eines Verzeichnisses zurück /// </summary> /// <param name="client"></param> /// <param name="folder"></param> /// <returns></returns> public List<FileReturn> GetFilesFromUpdateFolder(FTPSClient client, string folder) { List<FileReturn> ret=new List<FileReturn>(); //In Update Verzeichniss legen string updateFolder=folder+"/"+Globals.ClientID+"/"; IList<DirectoryListItem> updateFiles=client.GetDirectoryList(updateFolder); //TODO evt FTPException abfangen //Sicherheitspause falls gerade noch eine Datei angelegt wurde Thread.Sleep(500); //Updatedatei herunterladen und auswerten foreach(DirectoryListItem dli in updateFiles) { string filenameLocal=FileSystem.TempPath+dli.Name; client.GetFile(updateFolder+dli.Name, filenameLocal); string[] lines=File.ReadAllLines(filenameLocal); string filename=lines[0]; //Datei hinzufügen EFileType fileType=GetFileType(lines[1]); //Updatedatei löschen client.DeleteFile(updateFolder+dli.Name); } return ret; }
/// <summary> /// Holt einen Dateilock /// </summary> /// <param name="client"></param> /// <param name="file"></param> /// <returns></returns> public bool GetFileLock(FTPSClient client, string file) { file=file.Replace("/", "\\"); file="l-data/"+file; if(client.Exists(file)) //Auf Lock überprüfen { Thread.Sleep(500); if(client.Exists(file)) //Nochmal Auf Lock überprüfen { return false; //Lock konnte nicht geholt werden } } //Lock erzeugen string lockFilenameLocal=FileSystem.TempPath+FileSystem.GetFilename(file); string lockFilenameLocalCheck=FileSystem.TempPath+FileSystem.GetFilename(file)+".check"; List<string> lines=new List<string>(); lines.Add(Globals.ClientID); File.WriteAllLines(lockFilenameLocal, lines.ToArray()); client.PutFile(lockFilenameLocal, file); //Lock überprüfen client.GetFile(file, lockFilenameLocalCheck); string[] linesLockFile=File.ReadAllLines(lockFilenameLocalCheck); string lockID=linesLockFile[0]; if(Globals.ClientID==lockID) //Überprüfen ob das Lock ein Client Lock ist { return true; } else //Kein Client eigenes Lock { return false; } }
/// <summary> /// Holt einen Dateilock /// </summary> /// <param name="client"></param> /// <param name="file"></param> /// <returns>true = New hash / </returns> public bool AddHistoryHash(FTPSClient client, string file, string relFilename) { string fileNameFTP=relFilename.Replace("/", "\\"); fileNameFTP="h-data/"+fileNameFTP; string fileHash=Hash.SHA1.HashFile(file); if(client.Exists(fileNameFTP)) //Neuen Hash hinzufügen { string hashFilenameLocal=FileSystem.TempPath+FileSystem.GetFilename(fileNameFTP); client.GetFile(fileNameFTP, hashFilenameLocal); List<string> hashInfo=new List<string>(); hashInfo.AddRange(File.ReadAllLines(hashFilenameLocal)); if(hashInfo[0]==fileHash) { return false; } else { hashInfo.Add(fileHash); File.WriteAllLines(hashFilenameLocal, hashInfo.ToArray()); client.PutFile(hashFilenameLocal, fileNameFTP); return true; } } else //Hash Datei erzeugen { List<string> hashFile=new List<string>(); hashFile.Add(fileHash); //Repository Version string hashFilenameLocal=FileSystem.TempPath+"hash-"+Various.GetTimeID(); File.WriteAllLines(hashFilenameLocal, hashFile.ToArray()); client.PutFile(hashFilenameLocal, fileNameFTP); return true; } }