static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.ReadFrom.AppSettings()
.CreateLogger();
Log.Information("The global logger has been configured");
var commandLineParseResult = Parser.Default.ParseArguments<Options>(args);
var parsed = commandLineParseResult as Parsed<Options>;
if (parsed == null)
{
#if DEBUG
Log.Debug("Program Debug Enabled");
Console.WriteLine("Press enter to continue.");
Console.ReadLine();
#endif
return; // not parsed
}
Options = parsed.Value;
Log.Debug("{@Options}", Options);
Console.WriteLine("Let's Encrypt (Simple Windows ACME Client)");
BaseURI = Options.BaseURI;
if (Options.Test)
{
BaseURI = "https://acme-staging.api.letsencrypt.org/";
Log.Debug("Test paramater set: {BaseURI}", BaseURI);
}
if (Options.SAN)
{
Log.Debug("SAN Option Enabled: Running per site and not per host");
}
Console.WriteLine($"\nACME Server: {BaseURI}");
Log.Information("ACME Server: {BaseURI}", BaseURI);
if (!string.IsNullOrWhiteSpace(Options.CentralSSLStore))
{
Console.WriteLine("Using Centralized SSL Path: " + Options.CentralSSLStore);
Log.Information("Using Centralized SSL Path: {CentralSSLStore}", Options.CentralSSLStore);
CentralSSL = true;
}
settings = new Settings(clientName, BaseURI);
Log.Debug("{@settings}", settings);
configPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), clientName, CleanFileName(BaseURI));
Console.WriteLine("Config Folder: " + configPath);
Log.Information("Config Folder: {configPath}", configPath);
Directory.CreateDirectory(configPath);
certificatePath = Properties.Settings.Default.CertificatePath;
if (string.IsNullOrWhiteSpace(certificatePath))
{
certificatePath = configPath;
}
else
{
try
{
Directory.CreateDirectory(certificatePath);
}
catch (Exception ex)
{
Console.WriteLine($"Error creating the certificate directory, {certificatePath}. Defaulting to config path");
Log.Warning("Error creating the certificate directory, {certificatePath}. Defaulting to config path. Error: {@ex}", certificatePath, ex);
certificatePath = configPath;
}
}
Console.WriteLine("Certificate Folder: " + certificatePath);
Log.Information("Certificate Folder: {certificatePath}", certificatePath);
try
{
using (var signer = new RS256Signer())
{
signer.Init();
var signerPath = Path.Combine(configPath, "Signer");
if (File.Exists(signerPath))
{
Console.WriteLine($"Loading Signer from {signerPath}");
Log.Information("Loading Signer from {signerPath}", signerPath);
using (var signerStream = File.OpenRead(signerPath))
signer.Load(signerStream);
}
using (client = new AcmeClient(new Uri(BaseURI), new AcmeServerDirectory(), signer))
{
client.Init();
Console.WriteLine("\nGetting AcmeServerDirectory");
Log.Information("Getting AcmeServerDirectory");
client.GetDirectory(true);
var registrationPath = Path.Combine(configPath, "Registration");
if (File.Exists(registrationPath))
{
Console.WriteLine($"Loading Registration from {registrationPath}");
Log.Information("Loading Registration from {registrationPath}", registrationPath);
using (var registrationStream = File.OpenRead(registrationPath))
client.Registration = AcmeRegistration.Load(registrationStream);
}
else
{
Console.Write("Enter an email address (not public, used for renewal fail notices): ");
var email = Console.ReadLine().Trim();
var contacts = new string[] { };
if (!String.IsNullOrEmpty(email))
{
Log.Debug("Registration email: {email}", email);
email = "mailto:" + email;
contacts = new string[] { email };
}
Console.WriteLine("Calling Register");
Log.Information("Calling Register");
var registration = client.Register(contacts);
if (!Options.AcceptTOS && !Options.Renew)
{
Console.WriteLine($"Do you agree to {registration.TosLinkUri}? (Y/N) ");
if (!PromptYesNo())
return;
}
Console.WriteLine("Updating Registration");
Log.Information("Updating Registration");
client.UpdateRegistration(true, true);
Console.WriteLine("Saving Registration");
Log.Information("Saving Registration");
using (var registrationStream = File.OpenWrite(registrationPath))
client.Registration.Save(registrationStream);
Console.WriteLine("Saving Signer");
Log.Information("Saving Signer");
using (var signerStream = File.OpenWrite(signerPath))
signer.Save(signerStream);
}
if (Options.Renew)
{
CheckRenewals();
#if DEBUG
Console.WriteLine("Press enter to continue.");
Console.ReadLine();
#endif
return;
}
var targets = new List<Target>();
if (!Options.SAN)
{
foreach (var plugin in Target.Plugins.Values)
{
targets.AddRange(plugin.GetTargets());
}
}
else
{
foreach (var plugin in Target.Plugins.Values)
{
targets.AddRange(plugin.GetSites());
}
}
if (targets.Count == 0)
{
Console.WriteLine("No targets found.");
Log.Error("No targets found.");
}
else
{
int HostsPerPage = 50;
try
{
HostsPerPage = Properties.Settings.Default.HostsPerPage;
}
catch (Exception ex)
{
Log.Error("Error getting HostsPerPage setting, setting to default value. Error: {@ex}", ex);
}
var count = 1;
if (targets.Count > HostsPerPage)
{
do
{
if ((count + HostsPerPage) <= targets.Count)
{
int stop = count + HostsPerPage;
for (int i = count; i < stop; i++)
{
if (!Options.SAN)
{
Console.WriteLine($" {count}: {targets[count - 1]}");
}
else
{
Console.WriteLine($" {count}: SAN - {targets[count - 1]}");
}
count++;
}
}
else
{
for (int i = count; i <= targets.Count; i++)
{
if (!Options.SAN)
{
Console.WriteLine($" {count}: {targets[count - 1]}");
}
else
{
Console.WriteLine($" {count}: SAN - {targets[count - 1]}");
}
count++;
}
}
if (count < targets.Count)
{
Console.WriteLine(" Q: Quit");
Console.Write("Press enter to continue to next page ");
var continueResponse = Console.ReadLine().ToLowerInvariant();
switch (continueResponse)
{
case "q":
throw new Exception($"Requested to quit application");
default:
break;
}
}
}
while (count < targets.Count);
}
else
{
foreach (var binding in targets)
{
Console.WriteLine($" {count}: {binding}");
count++;
}
}
}
Console.WriteLine();
foreach (var plugin in Target.Plugins.Values)
{
plugin.PrintMenu();
}
Console.WriteLine(" A: Get certificates for all hosts");
Console.WriteLine(" Q: Quit");
Console.Write("Which host do you want to get a certificate for: ");
var response = Console.ReadLine().ToLowerInvariant();
switch (response)
{
case "a":
foreach (var target in targets)
{
Auto(target);
}
break;
case "q":
return;
default:
var targetId = 0;
if (Int32.TryParse(response, out targetId))
{
targetId--;
if (targetId >= 0 && targetId < targets.Count)
{
var binding = targets[targetId];
Auto(binding);
}
}
else
{
foreach (var plugin in Target.Plugins.Values)
{
plugin.HandleMenuResponse(response, targets);
}
}
break;
}
}
}
}
catch (Exception e)
{
Log.Error("Error {@e}", e);
Console.ForegroundColor = ConsoleColor.Red;
var acmeWebException = e as AcmeClient.AcmeWebException;
if (acmeWebException != null)
{
Console.WriteLine(acmeWebException.Message);
Console.WriteLine("ACME Server Returned:");
Console.WriteLine(acmeWebException.Response.ContentAsString);
}
else
{
Console.WriteLine(e);
}
Console.ResetColor();
}
Console.WriteLine("Press enter to continue.");
Console.ReadLine();
}