191202PX Added namespace dezentrale.core for the code running in background (thus, completing the mvc structure)
This commit is contained in:
parent
7a65a110fc
commit
20c32ab7a3
22
Program.cs
22
Program.cs
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using dezentrale.core;
|
||||
using dezentrale.model;
|
||||
using dezentrale.model.money;
|
||||
using dezentrale.view;
|
||||
|
@ -31,7 +32,7 @@ namespace dezentrale
|
|||
{
|
||||
public class Program
|
||||
{
|
||||
public static uint VersionNumber { get; private set; } = 0x19120100;
|
||||
public static uint VersionNumber { get; private set; } = 0x19120200;
|
||||
public static string VersionString { get; private set; } = $"{VersionNumber:x}";
|
||||
|
||||
public static string AppData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||
|
@ -184,10 +185,23 @@ namespace dezentrale
|
|||
if (!config.ImportExport.VerifyExport(config.DbDirectory, DmDirectory)) return 1;
|
||||
break;
|
||||
case eMode.Import:
|
||||
{
|
||||
ImportProcess import = new ImportProcess()
|
||||
{
|
||||
MemberImportSettings = Program.config.ImportExport,
|
||||
MemberDir = Program.config.DbDirectory,
|
||||
InputDir = Program.DmDirectory,
|
||||
};
|
||||
ConsoleLogger logger = new ConsoleLogger();
|
||||
System.Threading.Thread t = logger.StartRunProcess(import);
|
||||
if (t != null) t.Join();
|
||||
else return 1;
|
||||
|
||||
if (!config.ImportExport.Import(config.DbDirectory, DmDirectory))
|
||||
return 1;
|
||||
break;
|
||||
//frmProcessWithLog frmImport = new frmProcessWithLog(import, true);
|
||||
//if (!config.ImportExport.Import(config.DbDirectory, DmDirectory))
|
||||
//return 1;
|
||||
if(logger.DialogResult != DialogResult.OK) return 1;
|
||||
} break;
|
||||
case eMode.BankImport:
|
||||
if (string.IsNullOrEmpty(csvInput))
|
||||
{
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
namespace dezentrale.core
|
||||
{
|
||||
public abstract class BackgroundProcess
|
||||
{
|
||||
public IProcessController LogTarget { get; set; } = null;
|
||||
public bool CancelButton { get; protected set; } = false;
|
||||
public string Caption { get; protected set; } = "BackgroundProcess";
|
||||
public uint Steps { get; protected set; } = 1;
|
||||
|
||||
protected abstract bool Run();
|
||||
|
||||
private Thread thread = null;
|
||||
public Thread StartRun()
|
||||
{
|
||||
if (thread != null) return null;
|
||||
thread = new Thread(RunAbstract) { IsBackground = true };
|
||||
thread.Start();
|
||||
return thread;
|
||||
}
|
||||
private void RunAbstract()
|
||||
{
|
||||
LogTarget.ActionCompleted(Run());
|
||||
}
|
||||
}
|
||||
public class HelloWorldProcess : BackgroundProcess
|
||||
{
|
||||
public HelloWorldProcess()
|
||||
{
|
||||
Caption = "Hello World";
|
||||
Steps = 20;
|
||||
}
|
||||
protected override bool Run()
|
||||
{
|
||||
for (uint i = 0; i < 20; i++)
|
||||
{
|
||||
string stepName = ((i & 0x01) == 0) ? "Hello" : "World";
|
||||
LogTarget.StepStarted(i, stepName);
|
||||
LogTarget.LogLine(stepName, dezentrale.model.LogEvent.ELogLevel.Info, "HelloWorldProcess");
|
||||
Thread.Sleep(200);
|
||||
LogTarget.StepCompleted(i, stepName, (i & 0x01) == 0);
|
||||
Thread.Sleep(200);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
|
||||
using dezentrale.model;
|
||||
|
||||
namespace dezentrale.core
|
||||
{
|
||||
public class ExportProcess : ImportExportBase
|
||||
{
|
||||
public ExportProcess()
|
||||
{
|
||||
Caption = "Database export";
|
||||
Steps = 4;
|
||||
}
|
||||
|
||||
protected override bool Run()
|
||||
{
|
||||
string tmpFile = Path.Combine(OutputDir, MemberImportSettings.ZipFile);
|
||||
Console.WriteLine($"Export: Packing {tmpFile}");
|
||||
|
||||
if (!Directory.Exists(MemberDir))
|
||||
{
|
||||
Console.WriteLine($"Cannot find directory '{MemberDir}'");
|
||||
return false;
|
||||
}
|
||||
if (!Directory.Exists(OutputDir))
|
||||
{
|
||||
Console.WriteLine($"Cannot find directory '{OutputDir}'");
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
// Depending on the directory this could be very large and would require more attention
|
||||
// in a commercial package.
|
||||
string[] filenames = Directory.GetFiles(MemberDir, "*.xml");
|
||||
string outFile = Path.Combine(OutputDir, MemberImportSettings.ZipFile);
|
||||
if (File.Exists(outFile))
|
||||
{
|
||||
string backupName = $"{outFile}.bak";
|
||||
if (File.Exists(backupName)) File.Delete(backupName);
|
||||
File.Move(outFile, backupName);
|
||||
}
|
||||
|
||||
// 'using' statements guarantee the stream is closed properly which is a big source
|
||||
// of problems otherwise. Its exception safe as well which is great.
|
||||
using (ZipOutputStream s = new ZipOutputStream(File.Create(outFile)))
|
||||
{
|
||||
s.SetLevel(9); // 0 - store only to 9 - means best compression
|
||||
s.Password = MemberImportSettings.ZipPassword; //null is a desired value for "no encryption"
|
||||
|
||||
byte[] buffer = new byte[4096];
|
||||
|
||||
foreach (string file in filenames)
|
||||
{
|
||||
Console.WriteLine(file);
|
||||
// Using GetFileName makes the result compatible with XP
|
||||
// as the resulting path is not absolute.
|
||||
var entry = new ZipEntry(Path.GetFileName(file));
|
||||
|
||||
// Setup the entry data as required.
|
||||
|
||||
// Crc and size are handled by the library for seakable streams
|
||||
// so no need to do them here.
|
||||
|
||||
// Could also use the last write time or similar for the file.
|
||||
entry.DateTime = DateTime.Now;
|
||||
s.PutNextEntry(entry);
|
||||
|
||||
using (FileStream fs = File.OpenRead(file))
|
||||
{
|
||||
|
||||
// Using a fixed size buffer here makes no noticeable difference for output
|
||||
// but keeps a lid on memory usage.
|
||||
int sourceBytes;
|
||||
do
|
||||
{
|
||||
sourceBytes = fs.Read(buffer, 0, buffer.Length);
|
||||
s.Write(buffer, 0, sourceBytes);
|
||||
} while (sourceBytes > 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Finish/Close arent needed strictly as the using statement does this automatically
|
||||
|
||||
// Finish is important to ensure trailing information for a Zip file is appended. Without this
|
||||
// the created file would be invalid.
|
||||
s.Finish();
|
||||
|
||||
// Close is important to wrap things up and unlock the file.
|
||||
s.Close();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Exception during export: {ex.Message}");
|
||||
|
||||
// No need to rethrow the exception as for our purposes its handled.
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (MemberImportSettings.GpgEnabled)
|
||||
{
|
||||
Console.WriteLine($"Export: Using GPG to encrypt {Path.Combine(OutputDir, MemberImportSettings.GpgFile)}");
|
||||
if (!Gpg($"--output \"{Path.Combine(OutputDir, MemberImportSettings.GpgFile)}\" -c \"{tmpFile}\"", MemberImportSettings.GpgPassword)) return false;
|
||||
tmpFile = Path.Combine(OutputDir, MemberImportSettings.GpgFile);
|
||||
}
|
||||
if (MemberImportSettings.HgEnabled)
|
||||
{
|
||||
Console.WriteLine($"Export: Using HG to commit / push {tmpFile}");
|
||||
//this might fail as repo might be existing / file might be already in repo
|
||||
RunProcess("hg", "init", OutputDir);
|
||||
RunProcess("hg", $"add {tmpFile}", OutputDir);
|
||||
//now, committing is more interesting
|
||||
if (!RunProcess("hg",
|
||||
"commit"
|
||||
+ $" --message \"dezentrale-members.exe --mode=export\nProgram version={Program.VersionString}\""
|
||||
+ $" --user \"{MemberImportSettings.HgUserName}\"", OutputDir))
|
||||
return false;
|
||||
if (!RunProcess("hg", $"--config auth.rc.prefix={MemberImportSettings.HgURL} --config auth.rc.username={MemberImportSettings.HgUserName} --config auth.rc.password={MemberImportSettings.HgPassword} push {MemberImportSettings.HgURL}", OutputDir))
|
||||
return false;
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
|
||||
using dezentrale.model;
|
||||
|
||||
namespace dezentrale.core
|
||||
{
|
||||
public interface ILogger
|
||||
{
|
||||
void Clear();
|
||||
void LogRaw(string text);
|
||||
void LogLine(string text, LogEvent.ELogLevel logLevel = LogEvent.ELogLevel.Info, string module = "");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
namespace dezentrale.core
|
||||
{
|
||||
public interface IProcessController : ILogger
|
||||
{
|
||||
void ActionCompleted(bool success);
|
||||
void StepStarted(uint stepNumber = 0, string stepDescription = "Generic");
|
||||
void StepCompleted(uint stepNumber = 0, string stepDescription = "Generic", bool success = true);
|
||||
System.Windows.Forms.DialogResult DialogResult { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
namespace dezentrale.core
|
||||
{
|
||||
public abstract class ImportExportBase : BackgroundProcess
|
||||
{
|
||||
public model.MemberImportExport MemberImportSettings { get; set; } = null;
|
||||
public string MemberDir { get; set; } = null; //! Directory that holds the application settings
|
||||
public string InputDir { get; set; } = null; //! Import working directory
|
||||
public string OutputDir { get; set; } = null; //! Export working directory
|
||||
|
||||
public static bool Gpg(string args, string gpgPassword)
|
||||
{
|
||||
string argsFull = $"--batch --yes --passphrase \"{gpgPassword}\" {args}";
|
||||
return RunProcess("gpg", argsFull);
|
||||
}
|
||||
|
||||
public static bool RunProcess(string cmd, string args, string workingDir = ".", core.ILogger log = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (log != null)
|
||||
{
|
||||
log.LogLine($"Running {cmd}", model.LogEvent.ELogLevel.Info, "RunProcess");
|
||||
log.LogRaw($"args: {cmd} {args}");
|
||||
log.LogRaw($"dir: {workingDir}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"cmd: {cmd} {args}");
|
||||
Console.WriteLine($"dir: {workingDir}");
|
||||
}
|
||||
Process p = new Process();
|
||||
|
||||
p.StartInfo.FileName = cmd;
|
||||
p.StartInfo.Arguments = args;
|
||||
p.StartInfo.WorkingDirectory = workingDir;
|
||||
p.StartInfo.UseShellExecute = false;
|
||||
p.StartInfo.RedirectStandardOutput = true;
|
||||
p.StartInfo.RedirectStandardError = true;
|
||||
p.StartInfo.CreateNoWindow = true;
|
||||
p.Start();
|
||||
string stdout = p.StandardOutput.ReadToEnd();
|
||||
string stderr = p.StandardError.ReadToEnd();
|
||||
p.WaitForExit();
|
||||
if (stdout.Length > 0)
|
||||
{
|
||||
if (log != null) log.LogRaw(stdout);
|
||||
else
|
||||
{
|
||||
Console.WriteLine("stdout:");
|
||||
Console.WriteLine(stdout);
|
||||
Console.WriteLine();
|
||||
}
|
||||
}
|
||||
if (stderr.Length > 0)
|
||||
{
|
||||
if (log != null) log.LogRaw(stderr);
|
||||
else
|
||||
{
|
||||
Console.WriteLine("stderr:");
|
||||
Console.WriteLine(stderr);
|
||||
Console.WriteLine();
|
||||
}
|
||||
}
|
||||
return p.ExitCode == 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error: {ex.Message}");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
|
||||
using dezentrale.model;
|
||||
|
||||
namespace dezentrale.core
|
||||
{
|
||||
public class ImportProcess : ImportExportBase
|
||||
{
|
||||
public ImportProcess()
|
||||
{
|
||||
Caption = "Database import";
|
||||
Steps = 4;
|
||||
}
|
||||
protected override bool Run()
|
||||
{
|
||||
|
||||
LogTarget.StepStarted(0, "Preparing import");
|
||||
|
||||
if (MemberImportSettings == null)
|
||||
{
|
||||
LogTarget.LogLine("MemberImportExport data class not set.", LogEvent.ELogLevel.Error, "ImportProcess");
|
||||
return false;
|
||||
}
|
||||
if (MemberDir == null)
|
||||
{
|
||||
LogTarget.LogLine("Member directory not set.", LogEvent.ELogLevel.Error, "ImportProcess");
|
||||
return false;
|
||||
}
|
||||
if (InputDir == null)
|
||||
{
|
||||
LogTarget.LogLine("Input directory not set.", LogEvent.ELogLevel.Error, "ImportProcess");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
LogTarget.StepStarted(1, "Fetching repository");
|
||||
|
||||
if (MemberImportSettings.HgEnabled)
|
||||
{
|
||||
RunProcess("hg", "init", InputDir, LogTarget);
|
||||
RunProcess("hg", "update null", InputDir, LogTarget);
|
||||
if (!RunProcess("hg", $"--config auth.rc.prefix={MemberImportSettings.HgURL} --config auth.rc.username={MemberImportSettings.HgUserName} --config auth.rc.password={MemberImportSettings.HgPassword} pull {MemberImportSettings.HgURL}", InputDir, LogTarget))
|
||||
return false;
|
||||
if (!RunProcess("hg", $"update", InputDir, LogTarget))
|
||||
return false;
|
||||
}
|
||||
if (MemberImportSettings.GpgEnabled)
|
||||
{
|
||||
Console.WriteLine($"Import: Using GPG to decrypt {Path.Combine(InputDir, MemberImportSettings.GpgFile)}");
|
||||
if (!Gpg($"--output {Path.Combine(InputDir, MemberImportSettings.ZipFile)} --decrypt {Path.Combine(InputDir, MemberImportSettings.GpgFile)}", MemberImportSettings.GpgPassword)) return false;
|
||||
}
|
||||
if (!Directory.Exists(MemberDir))
|
||||
{
|
||||
LogTarget.LogLine($"Cannot find directory '{MemberDir}'", LogEvent.ELogLevel.Error, "ImportProcess");
|
||||
return false;
|
||||
}
|
||||
if (!Directory.Exists(InputDir))
|
||||
{
|
||||
LogTarget.LogLine($"Cannot find directory '{InputDir}'", LogEvent.ELogLevel.Error, "ImportProcess");
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
LogTarget.StepStarted(2, $"Backupping Contents of {MemberDir}");
|
||||
|
||||
string[] filenames = Directory.GetFiles(MemberDir, "*.xml");
|
||||
LogTarget.LogLine($"Renaming {filenames.Length} xml files to xml.bak in {MemberDir}", LogEvent.ELogLevel.Info, "ImportProcess");
|
||||
|
||||
foreach (string f in filenames)
|
||||
{
|
||||
|
||||
string fileName = Path.GetFileName(f);
|
||||
string backupName = $"{fileName}.bak";
|
||||
if (File.Exists(backupName))
|
||||
{
|
||||
LogTarget.LogLine($"Deleting previous {backupName}", LogEvent.ELogLevel.Trace, "ImportProcess");
|
||||
File.Delete(backupName);
|
||||
}
|
||||
LogTarget.LogLine($"Renaming {fileName} ({f}) to {backupName}", LogEvent.ELogLevel.Trace, "ImportProcess");
|
||||
File.Move(fileName, backupName);
|
||||
|
||||
LogTarget.LogLine($"Deleting backupped {fileName}", LogEvent.ELogLevel.Trace, "ImportProcess");
|
||||
File.Delete(fileName);
|
||||
}
|
||||
|
||||
LogTarget.StepStarted(3, $"Unpacking {MemberImportSettings.ZipFile}");
|
||||
LogTarget.LogLine($"Extracting {Path.Combine(InputDir, MemberImportSettings.ZipFile)}", LogEvent.ELogLevel.Info, "ImportProcess");
|
||||
string inFile = Path.Combine(InputDir, MemberImportSettings.ZipFile);
|
||||
|
||||
|
||||
using (ZipInputStream s = new ZipInputStream(File.OpenRead(inFile)))
|
||||
{
|
||||
s.Password = MemberImportSettings.ZipPassword;
|
||||
|
||||
ZipEntry theEntry;
|
||||
while ((theEntry = s.GetNextEntry()) != null)
|
||||
{
|
||||
|
||||
Console.WriteLine(theEntry.Name);
|
||||
|
||||
string directoryName = Path.GetDirectoryName(theEntry.Name);
|
||||
string fileName = Path.GetFileName(theEntry.Name);
|
||||
|
||||
// create directory
|
||||
if (directoryName.Length > 0)
|
||||
{
|
||||
Directory.CreateDirectory(directoryName);
|
||||
}
|
||||
|
||||
if (fileName != String.Empty)
|
||||
{
|
||||
using (FileStream streamWriter = File.Create(Path.Combine(MemberDir, theEntry.Name)))
|
||||
{
|
||||
|
||||
int size = 2048;
|
||||
byte[] data = new byte[2048];
|
||||
while (true)
|
||||
{
|
||||
size = s.Read(data, 0, data.Length);
|
||||
if (size > 0)
|
||||
{
|
||||
streamWriter.Write(data, 0, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LogTarget.StepCompleted(3, $"Unpacking {MemberImportSettings.ZipFile}", true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogTarget.LogLine($"Exception during import: {ex.Message}", LogEvent.ELogLevel.Error, "ImportProcess");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -183,6 +183,13 @@
|
|||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="view\frmProcessWithLog.cs" />
|
||||
<Compile Include="core\ImportProcess.cs" />
|
||||
<Compile Include="core\ILogger.cs" />
|
||||
<Compile Include="core\IProcessController.cs" />
|
||||
<Compile Include="core\BackgroundProcess.cs" />
|
||||
<Compile Include="view\ConsoleLogger.cs" />
|
||||
<Compile Include="core\ExportProcess.cs" />
|
||||
<Compile Include="core\ImportExportBase.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
|
@ -196,5 +203,8 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<ItemGroup />
|
||||
<ItemGroup>
|
||||
<Folder Include="core\" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
|
@ -6,159 +6,6 @@ using System.Diagnostics;
|
|||
|
||||
namespace dezentrale.model
|
||||
{
|
||||
public class ImportProcess : view.BackgroundProcess
|
||||
{
|
||||
public MemberImportExport MemberImportSettings { get; set; } = null;
|
||||
public string MemberDir { get; set; } = null;
|
||||
public string InputDir { get; set; } = null;
|
||||
public ImportProcess()
|
||||
{
|
||||
Caption = "Database import";
|
||||
Steps = 4;
|
||||
}
|
||||
protected override bool Run()
|
||||
{
|
||||
|
||||
LogTarget.StepStarted(0, "Preparing import");
|
||||
|
||||
if (MemberImportSettings == null)
|
||||
{
|
||||
LogTarget.LogLine("MemberImportExport data class not set.", LogEvent.ELogLevel.Error, "ImportProcess");
|
||||
return false;
|
||||
}
|
||||
if (MemberDir == null)
|
||||
{
|
||||
LogTarget.LogLine("Member directory not set.", LogEvent.ELogLevel.Error, "ImportProcess");
|
||||
return false;
|
||||
}
|
||||
if (InputDir == null)
|
||||
{
|
||||
LogTarget.LogLine("Input directory not set.", LogEvent.ELogLevel.Error, "ImportProcess");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
LogTarget.StepStarted(1, "Fetching repository");
|
||||
|
||||
if (MemberImportSettings.HgEnabled)
|
||||
{
|
||||
MemberImportExport.RunProcess("hg", "init", InputDir, LogTarget);
|
||||
MemberImportExport.RunProcess("hg", "update null", InputDir, LogTarget);
|
||||
if (!MemberImportExport.RunProcess("hg", $"--config auth.rc.prefix={MemberImportSettings.HgURL} --config auth.rc.username={MemberImportSettings.HgUserName} --config auth.rc.password={MemberImportSettings.HgPassword} pull {MemberImportSettings.HgURL}", InputDir, LogTarget))
|
||||
return false;
|
||||
if (!MemberImportExport.RunProcess("hg", $"update", InputDir, LogTarget))
|
||||
return false;
|
||||
}
|
||||
if (MemberImportSettings.GpgEnabled)
|
||||
{
|
||||
Console.WriteLine($"Import: Using GPG to decrypt {Path.Combine(InputDir, MemberImportSettings.GpgFile)}");
|
||||
if (!MemberImportExport.Gpg($"--output {Path.Combine(InputDir, MemberImportSettings.ZipFile)} --decrypt {Path.Combine(InputDir, MemberImportSettings.GpgFile)}", MemberImportSettings.GpgPassword)) return false;
|
||||
}
|
||||
if (!Directory.Exists(MemberDir))
|
||||
{
|
||||
LogTarget.LogLine($"Cannot find directory '{MemberDir}'", LogEvent.ELogLevel.Error, "ImportProcess");
|
||||
return false;
|
||||
}
|
||||
if (!Directory.Exists(InputDir))
|
||||
{
|
||||
LogTarget.LogLine($"Cannot find directory '{InputDir}'", LogEvent.ELogLevel.Error, "ImportProcess");
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
LogTarget.StepStarted(2, $"Backupping Contents of {MemberDir}");
|
||||
|
||||
string[] filenames = Directory.GetFiles(MemberDir, "*.xml");
|
||||
LogTarget.LogLine($"Renaming {filenames.Length} xml files to xml.bak in {MemberDir}", LogEvent.ELogLevel.Info, "ImportProcess");
|
||||
|
||||
foreach (string f in filenames)
|
||||
{
|
||||
string fileName = Path.GetFileName(f);
|
||||
string backupName = $"{fileName}.bak";
|
||||
if (File.Exists(backupName)) File.Delete(backupName);
|
||||
File.Move(fileName, backupName);
|
||||
File.Delete(fileName);
|
||||
}
|
||||
|
||||
LogTarget.StepStarted(3, $"Unpacking {MemberImportSettings.ZipFile}");
|
||||
LogTarget.LogLine($"Extracting {Path.Combine(InputDir, MemberImportSettings.ZipFile)}", LogEvent.ELogLevel.Info, "ImportProcess");
|
||||
string inFile = Path.Combine(InputDir, MemberImportSettings.ZipFile);
|
||||
|
||||
|
||||
using (ZipInputStream s = new ZipInputStream(File.OpenRead(inFile)))
|
||||
{
|
||||
s.Password = MemberImportSettings.ZipPassword;
|
||||
|
||||
ZipEntry theEntry;
|
||||
while ((theEntry = s.GetNextEntry()) != null)
|
||||
{
|
||||
|
||||
Console.WriteLine(theEntry.Name);
|
||||
|
||||
string directoryName = Path.GetDirectoryName(theEntry.Name);
|
||||
string fileName = Path.GetFileName(theEntry.Name);
|
||||
|
||||
// create directory
|
||||
if (directoryName.Length > 0)
|
||||
{
|
||||
Directory.CreateDirectory(directoryName);
|
||||
}
|
||||
|
||||
if (fileName != String.Empty)
|
||||
{
|
||||
using (FileStream streamWriter = File.Create(Path.Combine(MemberDir, theEntry.Name)))
|
||||
{
|
||||
|
||||
int size = 2048;
|
||||
byte[] data = new byte[2048];
|
||||
while (true)
|
||||
{
|
||||
size = s.Read(data, 0, data.Length);
|
||||
if (size > 0)
|
||||
{
|
||||
streamWriter.Write(data, 0, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LogTarget.StepCompleted(3, $"Unpacking {MemberImportSettings.ZipFile}", true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogTarget.LogLine($"Exception during import: {ex.Message}", LogEvent.ELogLevel.Error, "ImportProcess");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
private bool ZipImport()
|
||||
{
|
||||
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Exception during import: {ex.Message}");
|
||||
|
||||
// No need to rethrow the exception as for our purposes its handled.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public class MemberImportExport
|
||||
{
|
||||
[XmlElement] public string ZipFile { get; set; } = "fnord.zip";
|
||||
|
@ -174,97 +21,10 @@ namespace dezentrale.model
|
|||
[XmlElement] public string GitUserName { get; set; } = "";
|
||||
[XmlElement] public string GitPassword { get; set; } = "";
|
||||
|
||||
|
||||
|
||||
public static bool RunProcess(string cmd, string args, string workingDir = ".", view.ILogger log = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (log != null)
|
||||
{
|
||||
log.LogLine($"Running {cmd}", LogEvent.ELogLevel.Info, "RunProcess");
|
||||
log.LogRaw($"args: {cmd} {args}");
|
||||
log.LogRaw($"dir: {workingDir}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"cmd: {cmd} {args}");
|
||||
Console.WriteLine($"dir: {workingDir}");
|
||||
}
|
||||
Process p = new Process();
|
||||
|
||||
p.StartInfo.FileName = cmd;
|
||||
p.StartInfo.Arguments = args;
|
||||
p.StartInfo.WorkingDirectory = workingDir;
|
||||
p.StartInfo.UseShellExecute = false;
|
||||
p.StartInfo.RedirectStandardOutput = true;
|
||||
p.StartInfo.RedirectStandardError = true;
|
||||
p.StartInfo.CreateNoWindow = true;
|
||||
p.Start();
|
||||
string stdout = p.StandardOutput.ReadToEnd();
|
||||
string stderr = p.StandardError.ReadToEnd();
|
||||
p.WaitForExit();
|
||||
if (stdout.Length > 0)
|
||||
{
|
||||
if (log != null) log.LogRaw(stdout);
|
||||
else
|
||||
{
|
||||
Console.WriteLine("stdout:");
|
||||
Console.WriteLine(stdout);
|
||||
Console.WriteLine();
|
||||
}
|
||||
}
|
||||
if (stderr.Length > 0)
|
||||
{
|
||||
if (log != null) log.LogRaw(stderr);
|
||||
else
|
||||
{
|
||||
Console.WriteLine("stderr:");
|
||||
Console.WriteLine(stderr);
|
||||
Console.WriteLine();
|
||||
}
|
||||
}
|
||||
return p.ExitCode == 0;
|
||||
} catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error: {ex.Message}");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
public static bool Gpg(string args, string gpgPassword)
|
||||
{
|
||||
string argsFull = $"--batch --yes --passphrase \"{gpgPassword}\" {args}";
|
||||
return RunProcess("gpg", argsFull);
|
||||
}
|
||||
[Obsolete]
|
||||
public bool Export(string memberDirectory, string outputDir)
|
||||
{
|
||||
string tmpFile = Path.Combine(outputDir, ZipFile);
|
||||
Console.WriteLine($"Export: Packing {tmpFile}");
|
||||
if (ZipExport(memberDirectory, outputDir) != true) return false;
|
||||
if (GpgEnabled)
|
||||
{
|
||||
Console.WriteLine($"Export: Using GPG to encrypt {Path.Combine(outputDir, GpgFile)}");
|
||||
if (!Gpg($"--output \"{Path.Combine(outputDir, GpgFile)}\" -c \"{tmpFile}\"", GpgPassword)) return false;
|
||||
tmpFile = Path.Combine(outputDir, GpgFile);
|
||||
}
|
||||
if (HgEnabled)
|
||||
{
|
||||
Console.WriteLine($"Export: Using HG to commit / push {tmpFile}");
|
||||
//this might fail as repo might be existing / file might be already in repo
|
||||
RunProcess("hg", "init", outputDir);
|
||||
RunProcess("hg", $"add {tmpFile}", outputDir);
|
||||
//now, committing is more interesting
|
||||
if (!RunProcess("hg",
|
||||
"commit"
|
||||
+ $" --message \"dezentrale-members.exe --mode=export\nProgram version={Program.VersionString}\""
|
||||
+ $" --user \"{HgUserName}\"", outputDir))
|
||||
return false;
|
||||
if (!RunProcess("hg", $"--config auth.rc.prefix={HgURL} --config auth.rc.username={HgUserName} --config auth.rc.password={HgPassword} push {HgURL}", outputDir))
|
||||
return false;
|
||||
|
||||
}
|
||||
return true;
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
|
@ -308,6 +68,7 @@ namespace dezentrale.model
|
|||
}
|
||||
return true;
|
||||
}
|
||||
[Obsolete]
|
||||
public bool VerifyExport(string memberDirectory, string outputDir)
|
||||
{
|
||||
if (!Directory.Exists(memberDirectory))
|
||||
|
@ -386,172 +147,6 @@ namespace dezentrale.model
|
|||
return true;
|
||||
}
|
||||
|
||||
private bool ZipExport(string memberDirectory, string outputDir)
|
||||
{
|
||||
if (!Directory.Exists(memberDirectory))
|
||||
{
|
||||
Console.WriteLine($"Cannot find directory '{memberDirectory}'");
|
||||
return false;
|
||||
}
|
||||
if (!Directory.Exists(outputDir))
|
||||
{
|
||||
Console.WriteLine($"Cannot find directory '{outputDir}'");
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
// Depending on the directory this could be very large and would require more attention
|
||||
// in a commercial package.
|
||||
string[] filenames = Directory.GetFiles(memberDirectory, "*.xml");
|
||||
string outFile = Path.Combine(outputDir, ZipFile);
|
||||
if (File.Exists(outFile))
|
||||
{
|
||||
string backupName = $"{outFile}.bak";
|
||||
if (File.Exists(backupName)) File.Delete(backupName);
|
||||
File.Move(outFile, backupName);
|
||||
}
|
||||
|
||||
// 'using' statements guarantee the stream is closed properly which is a big source
|
||||
// of problems otherwise. Its exception safe as well which is great.
|
||||
using (ZipOutputStream s = new ZipOutputStream(File.Create(outFile)))
|
||||
{
|
||||
s.SetLevel(9); // 0 - store only to 9 - means best compression
|
||||
s.Password = ZipPassword; //null is a desired value for "no encryption"
|
||||
|
||||
byte[] buffer = new byte[4096];
|
||||
|
||||
foreach (string file in filenames)
|
||||
{
|
||||
Console.WriteLine(file);
|
||||
// Using GetFileName makes the result compatible with XP
|
||||
// as the resulting path is not absolute.
|
||||
var entry = new ZipEntry(Path.GetFileName(file));
|
||||
|
||||
// Setup the entry data as required.
|
||||
|
||||
// Crc and size are handled by the library for seakable streams
|
||||
// so no need to do them here.
|
||||
|
||||
// Could also use the last write time or similar for the file.
|
||||
entry.DateTime = DateTime.Now;
|
||||
s.PutNextEntry(entry);
|
||||
|
||||
using (FileStream fs = File.OpenRead(file))
|
||||
{
|
||||
|
||||
// Using a fixed size buffer here makes no noticeable difference for output
|
||||
// but keeps a lid on memory usage.
|
||||
int sourceBytes;
|
||||
do
|
||||
{
|
||||
sourceBytes = fs.Read(buffer, 0, buffer.Length);
|
||||
s.Write(buffer, 0, sourceBytes);
|
||||
} while (sourceBytes > 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Finish/Close arent needed strictly as the using statement does this automatically
|
||||
|
||||
// Finish is important to ensure trailing information for a Zip file is appended. Without this
|
||||
// the created file would be invalid.
|
||||
s.Finish();
|
||||
|
||||
// Close is important to wrap things up and unlock the file.
|
||||
s.Close();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Exception during export: {ex.Message}");
|
||||
|
||||
// No need to rethrow the exception as for our purposes its handled.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
[Obsolete]
|
||||
private bool ZipImport(string memberDirectory, string inputDir)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
/*
|
||||
if (!Directory.Exists(memberDirectory))
|
||||
{
|
||||
Console.WriteLine($"Cannot find directory '{memberDirectory}'");
|
||||
return false;
|
||||
}
|
||||
if (!Directory.Exists(inputDir))
|
||||
{
|
||||
Console.WriteLine($"Cannot find directory '{inputDir}'");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
//First, rename all member-*.xml to member-*.xml.bak
|
||||
string[] filenames = Directory.GetFiles(memberDirectory, "*.xml");
|
||||
foreach (string f in filenames)
|
||||
{
|
||||
string fileName = Path.GetFileName(f);
|
||||
string backupName = $"{fileName}.bak";
|
||||
if (File.Exists(backupName)) File.Delete(backupName);
|
||||
File.Move(fileName, backupName);
|
||||
File.Delete(fileName);
|
||||
}
|
||||
string inFile = Path.Combine(inputDir, ZipFile);
|
||||
using (ZipInputStream s = new ZipInputStream(File.OpenRead(inFile)))
|
||||
{
|
||||
s.Password = ZipPassword;
|
||||
|
||||
ZipEntry theEntry;
|
||||
while ((theEntry = s.GetNextEntry()) != null)
|
||||
{
|
||||
|
||||
Console.WriteLine(theEntry.Name);
|
||||
|
||||
string directoryName = Path.GetDirectoryName(theEntry.Name);
|
||||
string fileName = Path.GetFileName(theEntry.Name);
|
||||
|
||||
// create directory
|
||||
if (directoryName.Length > 0)
|
||||
{
|
||||
Directory.CreateDirectory(directoryName);
|
||||
}
|
||||
|
||||
if (fileName != String.Empty)
|
||||
{
|
||||
using (FileStream streamWriter = File.Create(Path.Combine(memberDirectory, theEntry.Name)))
|
||||
{
|
||||
|
||||
int size = 2048;
|
||||
byte[] data = new byte[2048];
|
||||
while (true)
|
||||
{
|
||||
size = s.Read(data, 0, data.Length);
|
||||
if (size > 0)
|
||||
{
|
||||
streamWriter.Write(data, 0, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Exception during import: {ex.Message}");
|
||||
|
||||
// No need to rethrow the exception as for our purposes its handled.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
using System;
|
||||
using dezentrale.model;
|
||||
|
||||
namespace dezentrale.view
|
||||
{
|
||||
public class ConsoleLogger : core.IProcessController
|
||||
{
|
||||
public System.Windows.Forms.DialogResult DialogResult { get; set; } = System.Windows.Forms.DialogResult.Cancel;
|
||||
public ConsoleLogger()
|
||||
{
|
||||
|
||||
}
|
||||
public System.Threading.Thread StartRunProcess(core.BackgroundProcess process)
|
||||
{
|
||||
Console.WriteLine($"Process - {process.Caption} (running)");
|
||||
|
||||
System.Threading.Thread t = process.StartRun();
|
||||
if(t == null)
|
||||
{
|
||||
Console.WriteLine($"Process - {process.Caption} (starting error)");
|
||||
}
|
||||
return t;
|
||||
}
|
||||
public void Clear()
|
||||
{
|
||||
Console.Clear();
|
||||
}
|
||||
|
||||
public void LogLine(string text, LogEvent.ELogLevel logLevel = LogEvent.ELogLevel.Info, string module = "")
|
||||
{
|
||||
Console.WriteLine($"{DateTime.Now} [{module}] {logLevel} {text}");
|
||||
}
|
||||
|
||||
public void LogRaw(string text)
|
||||
{
|
||||
Console.WriteLine(text);
|
||||
}
|
||||
|
||||
public void StepStarted(uint stepNumber = 0, string stepDescription = "Generic")
|
||||
{
|
||||
Console.WriteLine($"# {stepNumber + 1}: Started {stepDescription}");
|
||||
}
|
||||
|
||||
public void StepCompleted(uint stepNumber = 0, string stepDescription = "Generic", bool success = true)
|
||||
{
|
||||
Console.WriteLine($"# {stepNumber + 1}: Completed({success}) {stepDescription}");
|
||||
}
|
||||
public void ActionCompleted(bool success)
|
||||
{
|
||||
if (success) Console.WriteLine($"Process - done (OK)");
|
||||
else Console.WriteLine($"Process - done (ERROR)");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using dezentrale.core;
|
||||
using dezentrale.model;
|
||||
using dezentrale.model.money;
|
||||
|
||||
|
@ -134,7 +135,7 @@ namespace dezentrale.view
|
|||
|
||||
private void mnuMain_File_Import(object sender, EventArgs e)
|
||||
{
|
||||
DialogResult dr = MessageBox.Show("Overwrite database?", "Warning\nImporting database from external source will overwrite all\nlocal changes! Really import?", MessageBoxButtons.YesNo);
|
||||
DialogResult dr = MessageBox.Show("Warning\nImporting database from external source will overwrite all\nlocal changes! Really import?", "Overwrite local version of the database?", MessageBoxButtons.YesNo);
|
||||
if(dr == DialogResult.Yes)
|
||||
{
|
||||
ImportProcess import = new ImportProcess()
|
||||
|
@ -143,16 +144,16 @@ namespace dezentrale.view
|
|||
MemberDir = Program.config.DbDirectory,
|
||||
InputDir = Program.DmDirectory,
|
||||
};
|
||||
frmProcessWithLog frmImport = new frmProcessWithLog(import, false);
|
||||
frmProcessWithLog frmImport = new frmProcessWithLog(import, true);
|
||||
dr = frmImport.ShowDialog();
|
||||
MessageBox.Show("DialogResult=" + dr);
|
||||
//MessageBox.Show("DialogResult=" + dr);
|
||||
//Reload member list
|
||||
//Reload moneytransfers, if loaded
|
||||
/*if (!Program.config.ImportExport.Import(Program.config.DbDirectory, Program.DmDirectory))
|
||||
{
|
||||
MessageBox.Show("Import failed!");
|
||||
}*/
|
||||
Application.Restart();
|
||||
if(dr == DialogResult.OK) Application.Restart();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,70 +3,13 @@ using System.Collections.Generic;
|
|||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using dezentrale.core;
|
||||
using dezentrale.model;
|
||||
|
||||
namespace dezentrale.view
|
||||
{
|
||||
public interface ILogger
|
||||
{
|
||||
void Clear();
|
||||
void LogRaw(string text);
|
||||
void LogLine(string text, LogEvent.ELogLevel logLevel = LogEvent.ELogLevel.Info, string module = "");
|
||||
}
|
||||
|
||||
public interface IProcessController : ILogger
|
||||
{
|
||||
void ActionCompleted(bool success);
|
||||
void StepStarted(uint stepNumber = 0, string stepDescription = "Generic");
|
||||
void StepCompleted(uint stepNumber = 0, string stepDescription = "Generic", bool success = true);
|
||||
}
|
||||
|
||||
public abstract class BackgroundProcess
|
||||
{
|
||||
public IProcessController LogTarget { get; set; } = null;
|
||||
public bool CancelButton { get; protected set; } = false;
|
||||
public string Caption { get; protected set; } = "BackgroundProcess";
|
||||
public uint Steps { get; protected set; } = 1;
|
||||
|
||||
protected abstract bool Run();
|
||||
|
||||
private Thread thread = null;
|
||||
public bool StartRun()
|
||||
{
|
||||
if (thread != null) return false;
|
||||
thread = new Thread(RunAbstract) { IsBackground = true };
|
||||
thread.Start();
|
||||
return true;
|
||||
}
|
||||
private void RunAbstract()
|
||||
{
|
||||
LogTarget.ActionCompleted(Run());
|
||||
}
|
||||
}
|
||||
public class HelloWorldProcess : BackgroundProcess
|
||||
{
|
||||
public HelloWorldProcess()
|
||||
{
|
||||
Caption = "Hello World";
|
||||
Steps = 20;
|
||||
}
|
||||
protected override bool Run()
|
||||
{
|
||||
for(uint i = 0; i < 20; i++)
|
||||
{
|
||||
string stepName = ((i & 0x01) == 0) ? "Hello" : "World";
|
||||
LogTarget.StepStarted(i, stepName);
|
||||
LogTarget.LogLine(stepName, LogEvent.ELogLevel.Info, "HelloWorldProcess");
|
||||
Thread.Sleep(200);
|
||||
LogTarget.StepCompleted(i, stepName, (i & 0x01) == 0);
|
||||
Thread.Sleep(200);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public class frmProcessWithLog : Form, IProcessController
|
||||
{
|
||||
private DialogResult toReturn = DialogResult.Cancel;
|
||||
|
@ -111,13 +54,11 @@ namespace dezentrale.view
|
|||
this.Text = $"Process - {this.process.Caption} (waiting)";
|
||||
this.Width = 800;
|
||||
this.Height = 600;
|
||||
this.StartPosition = FormStartPosition.CenterParent;
|
||||
|
||||
this.FormClosing += (sender, e) => { DialogResult = toReturn; };
|
||||
this.Load += (sender, e) => { if (autoRun) btnStart_Click(null, null); };
|
||||
|
||||
/* if (autoRun)
|
||||
{
|
||||
MessageBox.Show("autoRun=true leads to problems with Invoke on gui access from background thread");
|
||||
this.Close();
|
||||
return;
|
||||
}*/
|
||||
// build GUI
|
||||
//[TextBox Multiline ]
|
||||
//[Step display label ]
|
||||
|
@ -184,11 +125,6 @@ namespace dezentrale.view
|
|||
btnClose.Height += 2;
|
||||
btnClose.Click += btnClose_Click;
|
||||
this.Controls.Add(btnClose);
|
||||
|
||||
if (autoRun)
|
||||
btnStart_Click(null,null);
|
||||
|
||||
this.FormClosing += (sender, e) => DialogResult = toReturn;
|
||||
}
|
||||
|
||||
private void btnStart_Click(object sender, EventArgs e)
|
||||
|
|
Loading…
Reference in New Issue