﻿B4J=true
Group=Default Group
ModulesStructureVersion=1
Type=Class
Version=10
@EndOfDesignText@
'Version: 1.00
Sub Class_Globals
	
	Private Const CertBotExe As String = "C:\Program Files\Certbot\bin\certbot.exe"
	Private Const OpenSSLExe As String = "c:\Program Files\Git\usr\bin\openssl.exe"
	
	Private CertBotWorkingFolder As String
	Private KeystorePassword As String
	Private WWWFolder As String
	Private Keytool As String
	Private CurrentKeyTime As Long
	Private CertificateFolder As String
	Private PrivateKeyFile As String = "privkey.pem"
	Private Const Alias As String = "b4j"
	Private Timer1 As Timer
End Sub

Public Sub Initialize
	WWWFolder = Main.srvr.StaticFilesFolder
	If File.Exists(WWWFolder, "") = False Then File.MakeDir(WWWFolder, "")
	CertBotWorkingFolder = File.Combine(File.DirApp, "certbot")
	File.MakeDir(CertBotWorkingFolder, "")
	CertificateFolder = File.Combine(CertBotWorkingFolder, "config/live/" & Main.Domain)
	Keytool = File.Combine(GetSystemProperty("java.home", ""), "bin/keytool")
	KeystorePassword = Main.KeystorePassword
	Log("Certificate folder: " & CertificateFolder & ", exists? " & File.Exists(CertificateFolder, ""))
	If File.Exists(CertificateFolder, PrivateKeyFile) Then
		CurrentKeyTime = File.LastModified(CertificateFolder, PrivateKeyFile)
		Log($"Private key time: $DateTime{CurrentKeyTime}"$)
	Else
		Log("Private key not found")
	End If
	Timer1.Initialize("Timer1", DateTime.TicksPerHour * 4)
	Timer1.Enabled = True
	Timer1_Tick
	StartMessageLoop
End Sub

Private Sub Timer1_Tick
	If CreateNewCertificate Then
		Log("new certificate created")
		ConvertToP12
		ConvertToKeystore
		ReloadCertificate
	End If
End Sub

'return true if time changed
Private Sub CreateNewCertificate As Boolean
	Dim params As List
	params.Initialize
	params.AddAll(Array("certonly", "--agree-tos", "--register-unsafely-without-email", "--noninteractive", "--webroot", "-w"))
	params.AddAll(Array(WWWFolder, "-d", Main.Domain))
	params.AddAll(Array("--work-dir", CertBotWorkingFolder))
	params.AddAll(Array("--logs-dir", File.Combine(CertBotWorkingFolder, "logs")))
	params.AddAll(Array("--config-dir", File.Combine(CertBotWorkingFolder, "config")))
	'uncomment to force renewal:
'	params.Add("--force-renewal") : Log("forcing renewal")
	Dim shl As Shell
	shl.Initialize("shl", CertBotExe, params)
	Dim res As ShellSyncResult = shl.RunSynchronous(-1)
	LogResult("certbot", res)
	If res.ExitCode = 0 Then
		Dim NewKeyTime As Long = File.LastModified(CertificateFolder, PrivateKeyFile)
		If NewKeyTime <> CurrentKeyTime Then
			CurrentKeyTime = NewKeyTime
			Return True
		Else
			Log("new key not created") 'can be commented.
		End If
	End If
	Return False
End Sub

Private Sub ConvertToP12
	Dim shl As Shell
	shl.Initialize("shl", OpenSSLExe, Array("pkcs12", "-export", "-in", "fullchain.pem", "-inkey", "privkey.pem", "-out", "server.p12", "-name", Alias , _
		"-passout", "pass:" & KeystorePassword))
	shl.WorkingDirectory = CertificateFolder
	Dim res As ShellSyncResult = shl.RunSynchronous(-1)
	LogResult("openssl", res)
End Sub

Private Sub ConvertToKeystore
	File.Delete(CertificateFolder, Main.KeystoreFilename)
	Dim shl As Shell
	shl.Initialize("shl", Keytool, Array("-importkeystore", "-deststorepass", KeystorePassword, "-destkeypass", KeystorePassword, _
		"-destkeystore", Main.KeystoreFilename, "-srckeystore", "server.p12", "-srcstoretype", "PKCS12", "-srcstorepass", KeystorePassword, "-alias", Alias))
	shl.WorkingDirectory = CertificateFolder
	Dim res As ShellSyncResult = shl.RunSynchronous(-1)
	LogResult("keytool", res)
	If res.ExitCode = 0 Then
		Dim target As String = File.Combine(File.DirApp, Main.KeystoreFilename)
		Log("copying new keystore to " & target)
		File.Copy(CertificateFolder, Main.KeystoreFilename, target, "")
	End If
End Sub

Private Sub LogResult(command As String, res As ShellSyncResult)
	If res.ExitCode <> 0 Then
		Log($"command' ${command}', exit code: ${res.ExitCode}
out: ${res.StdOut}
error: ${res.StdErr}"$)
	Else
		Log($"command '${command}' succeeded"$) 'can be commented.
	End If

End Sub

Private Sub ReloadCertificate
	If Main.SslConfiguration.IsInitialized = False Then
		Log("Main.SslConfiguration not initialized. Can't reload certificate")
		Return
	End If
	Dim Consumer As JavaObject
	Dim class As String = Me.as(JavaObject).RunMethod("getClass", Null).As(String).SubString("class ".Length)
	Consumer.InitializeNewInstance(class & "$MyConsumer", Null)
	Main.SslConfiguration.As(JavaObject).RunMethod("reload", Array(Consumer))
End Sub

#if JAVA
public static class MyConsumer implements java.util.function.Consumer<org.eclipse.jetty.util.ssl.SslContextFactory> {
	public void accept(org.eclipse.jetty.util.ssl.SslContextFactory ssl) {
		BA.Log("*** certificate reloaded! ***");
	}
}
#End If