/// <summary> /// this is usually only used for remote clients; standalone clients are patched with a windows installer program /// </summary> private static void CheckForPatches() { FSplashScreen.ProgressText = "Running checks that are specific to Remote Installation..."; // todo: check whether the user has SYSADMIN rights; should not be required // todo: check whether the user has write access to the bin directory // check whether the user has access to the server and the Petra patches directory if ((TClientSettings.Petra_Path_RemotePatches.Length > 0) && !(TClientSettings.Petra_Path_RemotePatches.ToLower().StartsWith("http://") || TClientSettings.Petra_Path_RemotePatches.ToLower().StartsWith("https://")) && !System.IO.Directory.Exists(TClientSettings.Petra_Path_RemotePatches)) { FSplashScreen.ShowMessageBox( String.Format( Catalog.GetString( "Please make sure that you have logged in to your network drive\nand can access the directory\n{0}\nIf this is the case and you still get this message,\nyou might use an IP address rather than a hostname for the server.\nPlease ask your local System Administrator for help."), TClientSettings.Petra_Path_RemotePatches), Catalog.GetString("Cannot check for patches")); } // check whether there is a patch available; if this is a remote version, try to download a patch from the server TPatchTools patchTools = new TPatchTools(Path.GetFullPath(TClientSettings.Petra_Path_Bin + Path.DirectorySeparatorChar + ".."), TClientSettings.Petra_Path_Bin, TPatchTools.OPENPETRA_VERSIONPREFIX, TClientSettings.PathTemp, "", TClientSettings.Petra_Path_Patches, TClientSettings.Petra_Path_RemotePatches); string PatchStatusMessage; // TODO: run this only if necessary. seem adding cost centre does not update the cache? TDataCache.ClearAllCaches(); if (patchTools.CheckForRecentPatch(false, out PatchStatusMessage)) { // todo: display a list of all patches that will be installed? or confusing with different builds? if (FSplashScreen.ShowMessageBox(String.Format(Catalog.GetString("There is a new patch available: {0}" + ".\r\nThe currently installed version is {1}" + ".\r\nThe patch will be installed to directory '{2}'.\r\nDo you want to install now?"), patchTools.GetLatestPatchVersion(), patchTools.GetCurrentPatchVersion(), TClientSettings.Petra_Path_Bin), String.Format(Catalog.GetString("Install new OpenPetra patch")), MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) == DialogResult.Yes) { // reset the caches in IsolatedStorage. This can help if things have changed drastically in the database // TODO: run this also after the software has been reinstalled with the InnoSetup installer? Remember the current patch number in the IsolatedStorage? TDataCache.ClearAllCaches(); // create the temp directory; using the Petra tmp directory, so that we don't need to change the drive in the batch file string TempPath = TClientSettings.PathTemp + Path.DirectorySeparatorChar + "petrapatch"; Directory.CreateDirectory(TempPath); // check for newer patchtool patchTools.CopyLatestPatchProgram(TempPath); string PatchToolExe = TempPath + Path.DirectorySeparatorChar + "Ict.Tools.PtchTool.exe"; if (!File.Exists(PatchToolExe)) { TLogging.Log("cannot find file " + PatchToolExe); } // need to stop petra client, start the patch in temppath, restart Petra client Process PatchProcess = new System.Diagnostics.Process(); PatchProcess.EnableRaisingEvents = false; PatchProcess.StartInfo.FileName = PatchToolExe; PatchProcess.StartInfo.Arguments = "-action:patchRemote " + "-ClientConfig:\"" + Path.GetFullPath( TAppSettingsManager.ConfigFileName) + "\" " + "-OpenPetra.Path.Patches:\"" + Path.GetFullPath( TClientSettings.Petra_Path_Bin + "/../patches30") + "\" " + "-OpenPetra.PathTemp:\"" + Path.GetFullPath( TClientSettings.Petra_Path_Bin + "/../tmp30") + "\" " + "-OpenPetra.Path:\"" + Path.GetFullPath( TClientSettings.Petra_Path_Bin + Path.DirectorySeparatorChar + "..") + "\" " + "-OpenPetra.Path.Bin:\"" + Path.GetFullPath( TClientSettings.Petra_Path_Bin) + "\""; PatchProcess.Start(); // Application stops here !!! Environment.Exit(0); } } else { if (PatchStatusMessage != String.Empty) { FSplashScreen.ShowMessageBox(PatchStatusMessage, ""); } } }
private void ApplyPatchRecursively(String APatchRootDirectory, String APatchDirectory) { string[] directories = System.IO.Directory.GetDirectories(APatchDirectory); foreach (string dir in directories) { ApplyPatchRecursively(APatchRootDirectory, dir); } TPatchTools patch = new TPatchTools(); string[] files = System.IO.Directory.GetFiles(APatchDirectory); foreach (string filename in files) { // what to do with the file: add, remove, patch string action; String TargetFile; // find a match with the registered File Patterns if ((!GetMatch(filename.Substring(APatchRootDirectory.Length + 1), out action, out TargetFile))) { throw new Exception("cannot find a destination path for file " + filename.Substring(APatchRootDirectory.Length + 1)); } // make sure that the path exists string TargetPath = Path.GetDirectoryName(TargetFile); if (!Directory.Exists(TargetPath)) { Directory.CreateDirectory(TargetPath); } // Console.WriteLine(filename + ' ' + TargetFile); if (action == "new") { if (System.IO.File.Exists(TargetFile)) { // prepare for undo; make a copy of the original file System.IO.File.Copy(TargetFile, TargetFile + ".bak", true); File.Delete(TargetFile); } // unzip the file BZip2.Decompress(System.IO.File.OpenRead(filename), System.IO.File.OpenWrite(TargetFile), true); } else if ((action == "patch") && File.Exists(TargetFile)) { try { // safety copy System.IO.File.Copy(TargetFile, TargetFile + ".bak", true); // apply patch if (System.IO.File.Exists(TargetFile + ".new")) { System.IO.File.Delete(TargetFile + ".new"); } patch.ApplyPatch(TargetFile, TargetFile + ".new", filename); // remove original, rename file if (System.IO.File.Exists(TargetFile + ".new")) { if (System.IO.File.Exists(TargetFile)) { System.IO.File.Delete(TargetFile); } System.IO.File.Move(TargetFile + ".new", TargetFile); } } catch (Exception e) { throw new Exception("problem patching file " + TargetFile + ": " + e.Message); } } else if (action == "rem") { if (System.IO.File.Exists(TargetFile)) { // safety copy System.IO.File.Copy(TargetFile, TargetFile + ".bak"); // remove file System.IO.File.Delete(TargetFile); } } else if (action == "skip") { // skip server files on a remote system } } }
private static void CreateDiffFiles(String ATmpDirectory, String ARootPatchDirectory, String AOldDirectory, String ANewDirectory, String ARecursiveSubDir) { string OldDirectory = AOldDirectory + '/' + ARecursiveSubDir; string NewDirectory = ANewDirectory + '/' + ARecursiveSubDir; string PatchDirectory = ARootPatchDirectory + '/' + ARecursiveSubDir; if (!Directory.Exists(PatchDirectory)) { Directory.CreateDirectory(PatchDirectory); } if (!Directory.Exists(OldDirectory)) { Directory.CreateDirectory(OldDirectory); } string[] directories = Directory.GetDirectories(NewDirectory); foreach (string dir in directories) { CreateDiffFiles(ATmpDirectory, ARootPatchDirectory, AOldDirectory, ANewDirectory, dir.Substring(ANewDirectory.Length + 1)); } // compare the files file by file TLogging.Log("enter directory " + OldDirectory, TLoggingType.ToConsole); string[] files = System.IO.Directory.GetFiles(OldDirectory); foreach (string filenameLoop in files) { bool OldPatchFileReused = false; string filename = Path.GetFileName(filenameLoop); if (System.IO.File.Exists(NewDirectory + '/' + filename)) { TPatchTools patch = new TPatchTools(); // compare if files are actually different if (!patch.IsSame(filenameLoop, NewDirectory + '/' + Path.GetFileName(filename))) { // create binary diff if (!Directory.Exists(PatchDirectory)) { Directory.CreateDirectory(PatchDirectory); } string OldPatchFile = ARootPatchDirectory + "bak" + PatchDirectory.Substring(ARootPatchDirectory.Length) + '/' + Path.GetFileName(filename) + ".patch"; if (System.IO.File.Exists(OldPatchFile)) { // could reuse, if checksum of the result is the same TPatchFileInfo OldPatchFileInfo; patch.ReadHeader(OldPatchFile, out OldPatchFileInfo); if (patch.CheckMd5Sum(NewDirectory + '/' + Path.GetFileName(filename), OldPatchFileInfo.NewMd5sum) && patch.CheckMd5Sum(filenameLoop, OldPatchFileInfo.OldMd5sum)) { // found a match, so no need for recreating the patch file; just copy it TLogging.Log("reusing diff " + OldPatchFile, TLoggingType.ToConsole); System.IO.File.Copy(OldPatchFile, PatchDirectory + '/' + Path.GetFileName(filename) + ".patch"); OldPatchFileReused = true; } } if (!OldPatchFileReused) { TLogging.Log("create diff " + PatchDirectory + '/' + Path.GetFileName(filename), TLoggingType.ToConsole); patch.CreateDiff(filenameLoop, NewDirectory + '/' + Path.GetFileName(filename), PatchDirectory + '/' + Path.GetFileName(filename) + ".patch"); } // if this is a file required for the patch program, include the new version if ((Path.GetFileName(filename) == "Ict.Common.dll") || (Path.GetFileName(filename) == "Ict.Common.IO.dll") || (Path.GetFileName(filename) == "ICSharpCode.SharpZipLib.dll") || (Path.GetFileName(filename).ToLower() == "Ict.Tools.PatchTool.exe".ToLower())) { // don't compress here: // the whole patch directory is zipped anyways in the end; // and it is easier to extract before running the patch System.IO.File.Copy(NewDirectory + '/' + Path.GetFileName(filename), PatchDirectory + '/' + Path.GetFileName( filename), true); } } } } // add new files (zip them) files = System.IO.Directory.GetFiles(NewDirectory); foreach (string filename in files) { if (!System.IO.File.Exists(OldDirectory + '/' + Path.GetFileName(filename))) { TLogging.Log("zip a new file " + NewDirectory + '/' + Path.GetFileName(filename), TLoggingType.ToConsole); if (!Directory.Exists(PatchDirectory)) { Directory.CreateDirectory(PatchDirectory); } try { BZip2.Compress(System.IO.File.OpenRead(NewDirectory + '/' + Path.GetFileName(filename)), System.IO.File.OpenWrite(PatchDirectory + '/' + Path.GetFileName(filename) + ".new"), true, 4096); } catch (Exception) { throw new Exception("Cannot write to file " + PatchDirectory + '/' + Path.GetFileName(filename) + ".new"); } } } // tell to remove files files = System.IO.Directory.GetFiles(OldDirectory); foreach (string filename in files) { if (!System.IO.File.Exists(NewDirectory + '/' + Path.GetFileName(filename))) { TLogging.Log("file removed: " + OldDirectory + '/' + Path.GetFileName(filename), TLoggingType.ToConsole); FileStream stream = System.IO.File.OpenWrite(PatchDirectory + '/' + Path.GetFileName(filename) + ".rem"); stream.Close(); } } // todo: something about changes to config files? xml file with instructions? // todo: check if all files can be matched when installing the patch }