add ProxyJump function
This commit is contained in:
1 parent
56e20e5464
commit
7f0e0fdb23
48 files changed
+3875
-44
No files matched your search
@@ -0,0 +1,260 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Runtime.Serialization.Json;
|
||||
using System.Text;
|
||||
|
||||
namespace BulkArchiveMailer.Core
|
||||
{
|
||||
[DataContract]
|
||||
public sealed class TemplateData
|
||||
{
|
||||
[DataMember(Name = "subject")]
|
||||
public string Subject { get; set; } = "";
|
||||
|
||||
[DataMember(Name = "bodyHtml")]
|
||||
public string BodyHtml { get; set; } = "";
|
||||
}
|
||||
|
||||
[DataContract]
|
||||
public sealed class TemplateSetData
|
||||
{
|
||||
[DataMember(Name = "ssh_access")]
|
||||
public TemplateData SshAccess { get; set; } = new TemplateData();
|
||||
|
||||
[DataMember(Name = "config_update")]
|
||||
public TemplateData ConfigUpdate { get; set; } = new TemplateData();
|
||||
}
|
||||
|
||||
/// <summary>Shape of %APPDATA%\BulkArchiveMailer\config.json (and of the embedded config.default.json that seeds it).</summary>
|
||||
[DataContract]
|
||||
public sealed class AppSettingsData
|
||||
{
|
||||
[DataMember(Name = "archiveFolder")]
|
||||
public string ArchiveFolder { get; set; } = "";
|
||||
|
||||
[DataMember(Name = "emailDomain")]
|
||||
public string EmailDomain { get; set; } = "redlab.lo";
|
||||
|
||||
/// <summary>Whether "test mode" (save to Drafts instead of Send) should be pre-checked when PreviewForm opens. Defaults to true - sending live is an explicit opt-in each time the operator unchecks it.</summary>
|
||||
[DataMember(Name = "testModeDefault")]
|
||||
public bool TestModeDefault { get; set; } = true;
|
||||
|
||||
/// <summary>Manual override for the Outlook signature .htm path, used when SignatureReader's registry-based auto-detection fails or the operator wants a different signature. Empty = rely on auto-detection.</summary>
|
||||
[DataMember(Name = "signatureHtmlPath")]
|
||||
public string SignatureHtmlPath { get; set; } = "";
|
||||
|
||||
[DataMember(Name = "templates")]
|
||||
public TemplateSetData Templates { get; set; } = new TemplateSetData();
|
||||
|
||||
public MailTemplateSet ToTemplateSet()
|
||||
{
|
||||
return new MailTemplateSet
|
||||
{
|
||||
SshAccess = new MailTemplate
|
||||
{
|
||||
Subject = Templates?.SshAccess?.Subject ?? "",
|
||||
BodyHtml = Templates?.SshAccess?.BodyHtml ?? "",
|
||||
},
|
||||
ConfigUpdate = new MailTemplate
|
||||
{
|
||||
Subject = Templates?.ConfigUpdate?.Subject ?? "",
|
||||
BodyHtml = Templates?.ConfigUpdate?.BodyHtml ?? "",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public void SetTemplate(ArchiveKind kind, MailTemplate template)
|
||||
{
|
||||
Templates = Templates ?? new TemplateSetData();
|
||||
var data = new TemplateData { Subject = template?.Subject ?? "", BodyHtml = template?.BodyHtml ?? "" };
|
||||
if (kind == ArchiveKind.SshAccess)
|
||||
{
|
||||
Templates.SshAccess = data;
|
||||
}
|
||||
else
|
||||
{
|
||||
Templates.ConfigUpdate = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads/saves the small JSON settings file at
|
||||
/// %APPDATA%\BulkArchiveMailer\config.json. On first run (file
|
||||
/// missing) the file is seeded verbatim from the config.default.json
|
||||
/// resource embedded in this assembly, so the shipped defaults and the
|
||||
/// initial user-editable file always start out identical - there is
|
||||
/// only one place (config.default.json) where default copy/domain
|
||||
/// text is authored.
|
||||
///
|
||||
/// Uses DataContractJsonSerializer (part of the .NET Framework since
|
||||
/// 3.5, ships in System.Runtime.Serialization.dll) rather than a
|
||||
/// third-party JSON package, specifically so this project has zero
|
||||
/// NuGet dependencies to resolve - this whole solution cannot be
|
||||
/// build-tested in the environment this was authored in, so removing
|
||||
/// every avoidable source of "does the package restore correctly on
|
||||
/// first open" risk is worth the small amount of extra code below.
|
||||
/// </summary>
|
||||
public static class AppSettings
|
||||
{
|
||||
private const string EmbeddedDefaultResourceName = "BulkArchiveMailer.Core.config.default.json";
|
||||
|
||||
public static string ConfigDirectory =>
|
||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "BulkArchiveMailer");
|
||||
|
||||
public static string ConfigFilePath => Path.Combine(ConfigDirectory, "config.json");
|
||||
|
||||
public static string ReadEmbeddedDefaultJson()
|
||||
{
|
||||
var asm = Assembly.GetExecutingAssembly();
|
||||
using (var stream = asm.GetManifestResourceStream(EmbeddedDefaultResourceName))
|
||||
{
|
||||
if (stream == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Embedded resource '{EmbeddedDefaultResourceName}' not found in {asm.FullName}. " +
|
||||
"Check that config.default.json's Build Action is set to Embedded Resource in BulkArchiveMailer.Core.csproj.");
|
||||
}
|
||||
|
||||
using (var reader = new StreamReader(stream, Encoding.UTF8))
|
||||
{
|
||||
return reader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static AppSettingsData LoadDefaults() => Deserialize(ReadEmbeddedDefaultJson());
|
||||
|
||||
/// <summary>
|
||||
/// Loads settings from disk, seeding the file from the embedded
|
||||
/// defaults on first run. A malformed hand-edited config.json is
|
||||
/// never silently overwritten - the DataContractJsonSerializer
|
||||
/// exception propagates so the caller can warn the operator
|
||||
/// instead of quietly discarding their edits.
|
||||
/// </summary>
|
||||
public static AppSettingsData Load()
|
||||
{
|
||||
if (!File.Exists(ConfigFilePath))
|
||||
{
|
||||
Directory.CreateDirectory(ConfigDirectory);
|
||||
File.WriteAllText(ConfigFilePath, ReadEmbeddedDefaultJson(), new UTF8Encoding(false));
|
||||
}
|
||||
|
||||
var json = File.ReadAllText(ConfigFilePath, Encoding.UTF8);
|
||||
return Deserialize(json);
|
||||
}
|
||||
|
||||
public static void Save(AppSettingsData data)
|
||||
{
|
||||
if (data == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(data));
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(ConfigDirectory);
|
||||
File.WriteAllText(ConfigFilePath, Serialize(data), new UTF8Encoding(false));
|
||||
}
|
||||
|
||||
private static AppSettingsData Deserialize(string json)
|
||||
{
|
||||
var bytes = Encoding.UTF8.GetBytes(json);
|
||||
using (var ms = new MemoryStream(bytes))
|
||||
{
|
||||
var serializer = new DataContractJsonSerializer(typeof(AppSettingsData));
|
||||
return (AppSettingsData)serializer.ReadObject(ms);
|
||||
}
|
||||
}
|
||||
|
||||
private static string Serialize(AppSettingsData data)
|
||||
{
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
var serializer = new DataContractJsonSerializer(typeof(AppSettingsData));
|
||||
serializer.WriteObject(ms, data);
|
||||
var minified = Encoding.UTF8.GetString(ms.ToArray());
|
||||
return PrettyPrintJson(minified);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DataContractJsonSerializer has no built-in indentation option
|
||||
/// and writes single-line JSON. This tiny formatter re-indents it
|
||||
/// so config.json stays comfortable to hand-edit (it is, after
|
||||
/// all, the officially supported way to change the mail
|
||||
/// templates). It only tracks string-literal boundaries, which is
|
||||
/// sufficient for the flat/shallow shape this file always has.
|
||||
/// </summary>
|
||||
private static string PrettyPrintJson(string json)
|
||||
{
|
||||
var sb = new StringBuilder(json.Length * 2);
|
||||
var indent = 0;
|
||||
var inString = false;
|
||||
var escapeNext = false;
|
||||
|
||||
for (var i = 0; i < json.Length; i++)
|
||||
{
|
||||
var c = json[i];
|
||||
|
||||
if (inString)
|
||||
{
|
||||
sb.Append(c);
|
||||
if (escapeNext)
|
||||
{
|
||||
escapeNext = false;
|
||||
}
|
||||
else if (c == '\\')
|
||||
{
|
||||
escapeNext = true;
|
||||
}
|
||||
else if (c == '"')
|
||||
{
|
||||
inString = false;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '"':
|
||||
inString = true;
|
||||
sb.Append(c);
|
||||
break;
|
||||
case '{':
|
||||
case '[':
|
||||
sb.Append(c);
|
||||
if (i + 1 < json.Length && (json[i + 1] == '}' || json[i + 1] == ']'))
|
||||
{
|
||||
sb.Append(json[i + 1]);
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
indent++;
|
||||
sb.Append('\n').Append(' ', indent * 2);
|
||||
}
|
||||
|
||||
break;
|
||||
case '}':
|
||||
case ']':
|
||||
indent--;
|
||||
sb.Append('\n').Append(' ', indent * 2).Append(c);
|
||||
break;
|
||||
case ',':
|
||||
sb.Append(c).Append('\n').Append(' ', indent * 2);
|
||||
break;
|
||||
case ':':
|
||||
sb.Append(c).Append(' ');
|
||||
break;
|
||||
default:
|
||||
sb.Append(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user