<commit_before>package launcher

import (
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"sync"

	"github.com/HouzuoGuo/laitos/daemon/autounlock"
	"github.com/HouzuoGuo/laitos/daemon/common"
	"github.com/HouzuoGuo/laitos/daemon/dnsd"
	"github.com/HouzuoGuo/laitos/daemon/httpd"
	"github.com/HouzuoGuo/laitos/daemon/httpd/handler"
	"github.com/HouzuoGuo/laitos/daemon/maintenance"
	"github.com/HouzuoGuo/laitos/daemon/plainsocket"
	"github.com/HouzuoGuo/laitos/daemon/simpleipsvcd"
	"github.com/HouzuoGuo/laitos/daemon/smtpd"
	"github.com/HouzuoGuo/laitos/daemon/smtpd/mailcmd"
	"github.com/HouzuoGuo/laitos/daemon/snmpd"
	"github.com/HouzuoGuo/laitos/daemon/sockd"
	"github.com/HouzuoGuo/laitos/daemon/telegrambot"
	"github.com/HouzuoGuo/laitos/inet"
	"github.com/HouzuoGuo/laitos/lalog"
	"github.com/HouzuoGuo/laitos/toolbox"
	"github.com/HouzuoGuo/laitos/toolbox/filter"
)

/*
StandardFilters contains a standard set of filters (PIN match, notification, lint, etc) that are useful to nearly all
laitos daemons that carry a command processor. The filters' configuration is fed by Config struct, which is itself fed
by deserialised JSON text.
*/
type StandardFilters struct {
	// For input command content
	TranslateSequences filter.TranslateSequences `json:"TranslateSequences"`
	PINAndShortcuts    filter.PINAndShortcuts    `json:"PINAndShortcuts"`

	// For command execution result
	NotifyViaEmail filter.NotifyViaEmail `json:"NotifyViaEmail"`
	LintText       filter.LintText       `json:"LintText"`
}

// Configure path to HTTP handlers and handler themselves.
type HTTPHandlers struct {
	InformationEndpoint string `json:"InformationEndpoint"`

	BrowserPhantomJSEndpoint       string                         `json:"BrowserPhantomJSEndpoint"`
	BrowserPhantomJSEndpointConfig handler.HandleBrowserPhantomJS `json:"BrowserPhantomJSEndpointConfig"`

	BrowserSlimerJSEndpoint       string                        `json:"BrowserSlimerJSEndpoint"`
	BrowserSlimerJSEndpointConfig handler.HandleBrowserSlimerJS `json:"BrowserSlimerJSEndpointConfig"`

	CommandFormEndpoint string `json:"CommandFormEndpoint"`

	GitlabBrowserEndpoint       string                      `json:"GitlabBrowserEndpoint"`
	GitlabBrowserEndpointConfig handler.HandleGitlabBrowser `json:"GitlabBrowserEndpointConfig"`

	IndexEndpoints      []string                   `json:"IndexEndpoints"`
	IndexEndpointConfig handler.HandleHTMLDocument `json:"IndexEndpointConfig"`

	MailMeEndpoint       string               `json:"MailMeEndpoint"`
	MailMeEndpointConfig handler.HandleMailMe `json:"MailMeEndpointConfig"`

	MicrosoftBotEndpoint1       string                     `json:"MicrosoftBotEndpoint1"`
	MicrosoftBotEndpointConfig1 handler.HandleMicrosoftBot `json:"MicrosoftBotEndpointConfig1"`
	MicrosoftBotEndpoint2       string                     `json:"MicrosoftBotEndpoint2"`
	MicrosoftBotEndpointConfig2 handler.HandleMicrosoftBot `json:"MicrosoftBotEndpointConfig2"`
	MicrosoftBotEndpoint3       string                     `json:"MicrosoftBotEndpoint3"`
	MicrosoftBotEndpointConfig3 handler.HandleMicrosoftBot `json:"MicrosoftBotEndpointConfig3"`

	RecurringCommandsEndpoint       string                          `json:"RecurringCommandsEndpoint"`
	RecurringCommandsEndpointConfig handler.HandleRecurringCommands `json:"RecurringCommandsEndpointConfig"`

	WebProxyEndpoint string `json:"WebProxyEndpoint"`

	TwilioSMSEndpoint        string                       `json:"TwilioSMSEndpoint"`
	TwilioCallEndpoint       string                       `json:"TwilioCallEndpoint"`
	TwilioCallEndpointConfig handler.HandleTwilioCallHook `json:"TwilioCallEndpointConfig"`
}

// The structure is JSON-compatible and capable of setting up all features and front-end services.
type Config struct {
	/*
		Features are toolbox feature instances shared by all daemons and command runners. Avoid duplicating this
		structure because certain toolbox features (such as AES file decryption) may hold large amount of data in
		memory. Therefore, all daemon preparation and initialisation routines operate on reference to this FeatureSet.
	*/
	Features   *toolbox.FeatureSet `json:"Features"`
	MailClient inet.MailClient     `json:"MailClient"` // MailClient is the common client configuration for sending notification emails and mail command runner results.

	Maintenance *maintenance.Daemon `json:"Maintenance"` // Daemon configures behaviour of periodic health-check/system maintenance

	DNSDaemon  *dnsd.Daemon    `json:"DNSDaemon"`  // DNSDaemon: configure DNS daemon's network behaviour
	DNSFilters StandardFilters `json:"DNSFilters"` // DNSFilters: configure DNS daemon's toolbox command processor

	HTTPDaemon   *httpd.Daemon   `json:"HTTPDaemon"`   // HTTP daemon configuration
	HTTPFilters  StandardFilters `json:"HTTPFilters"`  // HTTP daemon filter configuration
	HTTPHandlers HTTPHandlers    `json:"HTTPHandlers"` // HTTP daemon handler configuration

	MailDaemon        *smtpd.Daemon          `json:"MailDaemon"`        // SMTP daemon configuration
	MailCommandRunner *mailcmd.CommandRunner `json:"MailCommandRunner"` // MailCommandRunner processes toolbox commands from incoming mail body.

	MailFilters StandardFilters `json:"MailFilters"` // MailFilters configure command processor for mail command runner

	PlainSocketDaemon  *plainsocket.Daemon `json:"PlainSocketDaemon"`  // Plain text protocol TCP and UDP daemon configuration
	PlainSocketFilters StandardFilters     `json:"PlainSocketFilters"` // Plain text daemon filter configuration

	SockDaemon *sockd.Daemon `json:"SockDaemon"` // Intentionally undocumented

	SNMPDaemon *snmpd.Daemon `json:"SNMPDaemon"` // SNMPDaemon configuration and instance

	SimpleIPSvcDaemon *simpleipsvcd.Daemon `json:"SimpleIPSvcDaemon"` // SimpleIPSvcDaemon is the simple TCP/UDP service daemon configuration and instance

	TelegramBot     *telegrambot.Daemon `json:"TelegramBot"`     // Telegram bot configuration
	TelegramFilters StandardFilters     `json:"TelegramFilters"` // Telegram bot filter configuration

	AutoUnlock *autounlock.Daemon `json:"AutoUnlock"` // AutoUnlock daemon

	SupervisorNotificationRecipients []string `json:"SupervisorNotificationRecipients"` // Email addresses of supervisor notification recipients

	logger                lalog.Logger // logger handles log output from configuration serialisation and initialisation routines.
	maintenanceInit       *sync.Once
	dnsDaemonInit         *sync.Once
	snmpDaemonInit        *sync.Once
	simpleIPSvcDaemonInit *sync.Once
	httpDaemonInit        *sync.Once
	mailCommandRunnerInit *sync.Once
	mailDaemonInit        *sync.Once
	plainSocketDaemonInit *sync.Once
	sockDaemonInit        *sync.Once
	telegramBotInit       *sync.Once
	autoUnlockInit        *sync.Once
}

// Initialise decorates feature configuration and command bridge configuration in preparation for daemon operations.
func (config *Config) Initialise() error {
	/*
		Fill in some blanks so that Get**** functions will be able to call Initialise() function, which in turn
		returns an error with meaningful message telling user that daemon is lacking configuration and will not start.
		If the nil daemons are not empty, user will only see a panic caused by nil, which is not very helpful.

		For the config.Features case, an empty FeatureSet can still offer several useful features such as program
		environment control and public institution contacts.
	*/
	if config.Features == nil {
		config.Features = &toolbox.FeatureSet{}
	}
	config.mailCommandRunnerInit = new(sync.Once)
	if config.MailCommandRunner == nil {
		config.MailCommandRunner = &mailcmd.CommandRunner{}
	}
	config.maintenanceInit = new(sync.Once)
	if config.Maintenance == nil {
		config.Maintenance = &maintenance.Daemon{}
	}
	config.dnsDaemonInit = new(sync.Once)
	if config.DNSDaemon == nil {
		config.DNSDaemon = &dnsd.Daemon{}
	}
	config.httpDaemonInit = new(sync.Once)
	if config.HTTPDaemon == nil {
		config.HTTPDaemon = &httpd.Daemon{}
	}
	config.mailDaemonInit = new(sync.Once)
	if config.MailDaemon == nil {
		config.MailDaemon = &smtpd.Daemon{}
	}
	config.plainSocketDaemonInit = new(sync.Once)
	if config.PlainSocketDaemon == nil {
		config.PlainSocketDaemon = &plainsocket.Daemon{}
	}
	config.simpleIPSvcDaemonInit = new(sync.Once)
	if config.SimpleIPSvcDaemon == nil {
		config.SimpleIPSvcDaemon = &simpleipsvcd.Daemon{}
	}
	config.snmpDaemonInit = new(sync.Once)
	if config.SNMPDaemon == nil {
		config.SNMPDaemon = &snmpd.Daemon{}
	}
	config.sockDaemonInit = new(sync.Once)
	if config.SockDaemon == nil {
		config.SockDaemon = &sockd.Daemon{}
	}
	config.telegramBotInit = new(sync.Once)
	if config.TelegramBot == nil {
		config.TelegramBot = &telegrambot.Daemon{}
	}
	config.autoUnlockInit = new(sync.Once)
	if config.AutoUnlock == nil {
		config.AutoUnlock = &autounlock.Daemon{}
	}
	// All notification filters share the common mail client
	config.HTTPFilters.NotifyViaEmail.MailClient = config.MailClient
	config.MailFilters.NotifyViaEmail.MailClient = config.MailClient<commit_msg>Offer the common mail client configuration to DNS toolbox command processor<commit_after>package launcher

import (
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"sync"

	"github.com/HouzuoGuo/laitos/daemon/autounlock"
	"github.com/HouzuoGuo/laitos/daemon/common"
	"github.com/HouzuoGuo/laitos/daemon/dnsd"
	"github.com/HouzuoGuo/laitos/daemon/httpd"
	"github.com/HouzuoGuo/laitos/daemon/httpd/handler"
	"github.com/HouzuoGuo/laitos/daemon/maintenance"
	"github.com/HouzuoGuo/laitos/daemon/plainsocket"
	"github.com/HouzuoGuo/laitos/daemon/simpleipsvcd"
	"github.com/HouzuoGuo/laitos/daemon/smtpd"
	"github.com/HouzuoGuo/laitos/daemon/smtpd/mailcmd"
	"github.com/HouzuoGuo/laitos/daemon/snmpd"
	"github.com/HouzuoGuo/laitos/daemon/sockd"
	"github.com/HouzuoGuo/laitos/daemon/telegrambot"
	"github.com/HouzuoGuo/laitos/inet"
	"github.com/HouzuoGuo/laitos/lalog"
	"github.com/HouzuoGuo/laitos/toolbox"
	"github.com/HouzuoGuo/laitos/toolbox/filter"
)

/*
StandardFilters contains a standard set of filters (PIN match, notification, lint, etc) that are useful to nearly all
laitos daemons that carry a command processor. The filters' configuration is fed by Config struct, which is itself fed
by deserialised JSON text.
*/
type StandardFilters struct {
	// For input command content
	TranslateSequences filter.TranslateSequences `json:"TranslateSequences"`
	PINAndShortcuts    filter.PINAndShortcuts    `json:"PINAndShortcuts"`

	// For command execution result
	NotifyViaEmail filter.NotifyViaEmail `json:"NotifyViaEmail"`
	LintText       filter.LintText       `json:"LintText"`
}

// Configure path to HTTP handlers and handler themselves.
type HTTPHandlers struct {
	InformationEndpoint string `json:"InformationEndpoint"`

	BrowserPhantomJSEndpoint       string                         `json:"BrowserPhantomJSEndpoint"`
	BrowserPhantomJSEndpointConfig handler.HandleBrowserPhantomJS `json:"BrowserPhantomJSEndpointConfig"`

	BrowserSlimerJSEndpoint       string                        `json:"BrowserSlimerJSEndpoint"`
	BrowserSlimerJSEndpointConfig handler.HandleBrowserSlimerJS `json:"BrowserSlimerJSEndpointConfig"`

	CommandFormEndpoint string `json:"CommandFormEndpoint"`

	GitlabBrowserEndpoint       string                      `json:"GitlabBrowserEndpoint"`
	GitlabBrowserEndpointConfig handler.HandleGitlabBrowser `json:"GitlabBrowserEndpointConfig"`

	IndexEndpoints      []string                   `json:"IndexEndpoints"`
	IndexEndpointConfig handler.HandleHTMLDocument `json:"IndexEndpointConfig"`

	MailMeEndpoint       string               `json:"MailMeEndpoint"`
	MailMeEndpointConfig handler.HandleMailMe `json:"MailMeEndpointConfig"`

	MicrosoftBotEndpoint1       string                     `json:"MicrosoftBotEndpoint1"`
	MicrosoftBotEndpointConfig1 handler.HandleMicrosoftBot `json:"MicrosoftBotEndpointConfig1"`
	MicrosoftBotEndpoint2       string                     `json:"MicrosoftBotEndpoint2"`
	MicrosoftBotEndpointConfig2 handler.HandleMicrosoftBot `json:"MicrosoftBotEndpointConfig2"`
	MicrosoftBotEndpoint3       string                     `json:"MicrosoftBotEndpoint3"`
	MicrosoftBotEndpointConfig3 handler.HandleMicrosoftBot `json:"MicrosoftBotEndpointConfig3"`

	RecurringCommandsEndpoint       string                          `json:"RecurringCommandsEndpoint"`
	RecurringCommandsEndpointConfig handler.HandleRecurringCommands `json:"RecurringCommandsEndpointConfig"`

	WebProxyEndpoint string `json:"WebProxyEndpoint"`

	TwilioSMSEndpoint        string                       `json:"TwilioSMSEndpoint"`
	TwilioCallEndpoint       string                       `json:"TwilioCallEndpoint"`
	TwilioCallEndpointConfig handler.HandleTwilioCallHook `json:"TwilioCallEndpointConfig"`
}

// The structure is JSON-compatible and capable of setting up all features and front-end services.
type Config struct {
	/*
		Features are toolbox feature instances shared by all daemons and command runners. Avoid duplicating this
		structure because certain toolbox features (such as AES file decryption) may hold large amount of data in
		memory. Therefore, all daemon preparation and initialisation routines operate on reference to this FeatureSet.
	*/
	Features   *toolbox.FeatureSet `json:"Features"`
	MailClient inet.MailClient     `json:"MailClient"` // MailClient is the common client configuration for sending notification emails and mail command runner results.

	Maintenance *maintenance.Daemon `json:"Maintenance"` // Daemon configures behaviour of periodic health-check/system maintenance

	DNSDaemon  *dnsd.Daemon    `json:"DNSDaemon"`  // DNSDaemon: configure DNS daemon's network behaviour
	DNSFilters StandardFilters `json:"DNSFilters"` // DNSFilters: configure DNS daemon's toolbox command processor

	HTTPDaemon   *httpd.Daemon   `json:"HTTPDaemon"`   // HTTP daemon configuration
	HTTPFilters  StandardFilters `json:"HTTPFilters"`  // HTTP daemon filter configuration
	HTTPHandlers HTTPHandlers    `json:"HTTPHandlers"` // HTTP daemon handler configuration

	MailDaemon        *smtpd.Daemon          `json:"MailDaemon"`        // SMTP daemon configuration
	MailCommandRunner *mailcmd.CommandRunner `json:"MailCommandRunner"` // MailCommandRunner processes toolbox commands from incoming mail body.

	MailFilters StandardFilters `json:"MailFilters"` // MailFilters configure command processor for mail command runner

	PlainSocketDaemon  *plainsocket.Daemon `json:"PlainSocketDaemon"`  // Plain text protocol TCP and UDP daemon configuration
	PlainSocketFilters StandardFilters     `json:"PlainSocketFilters"` // Plain text daemon filter configuration

	SockDaemon *sockd.Daemon `json:"SockDaemon"` // Intentionally undocumented

	SNMPDaemon *snmpd.Daemon `json:"SNMPDaemon"` // SNMPDaemon configuration and instance

	SimpleIPSvcDaemon *simpleipsvcd.Daemon `json:"SimpleIPSvcDaemon"` // SimpleIPSvcDaemon is the simple TCP/UDP service daemon configuration and instance

	TelegramBot     *telegrambot.Daemon `json:"TelegramBot"`     // Telegram bot configuration
	TelegramFilters StandardFilters     `json:"TelegramFilters"` // Telegram bot filter configuration

	AutoUnlock *autounlock.Daemon `json:"AutoUnlock"` // AutoUnlock daemon

	SupervisorNotificationRecipients []string `json:"SupervisorNotificationRecipients"` // Email addresses of supervisor notification recipients

	logger                lalog.Logger // logger handles log output from configuration serialisation and initialisation routines.
	maintenanceInit       *sync.Once
	dnsDaemonInit         *sync.Once
	snmpDaemonInit        *sync.Once
	simpleIPSvcDaemonInit *sync.Once
	httpDaemonInit        *sync.Once
	mailCommandRunnerInit *sync.Once
	mailDaemonInit        *sync.Once
	plainSocketDaemonInit *sync.Once
	sockDaemonInit        *sync.Once
	telegramBotInit       *sync.Once
	autoUnlockInit        *sync.Once
}

// Initialise decorates feature configuration and command bridge configuration in preparation for daemon operations.
func (config *Config) Initialise() error {
	/*
		Fill in some blanks so that Get**** functions will be able to call Initialise() function, which in turn
		returns an error with meaningful message telling user that daemon is lacking configuration and will not start.
		If the nil daemons are not empty, user will only see a panic caused by nil, which is not very helpful.

		For the config.Features case, an empty FeatureSet can still offer several useful features such as program
		environment control and public institution contacts.
	*/
	if config.Features == nil {
		config.Features = &toolbox.FeatureSet{}
	}
	config.mailCommandRunnerInit = new(sync.Once)
	if config.MailCommandRunner == nil {
		config.MailCommandRunner = &mailcmd.CommandRunner{}
	}
	config.maintenanceInit = new(sync.Once)
	if config.Maintenance == nil {
		config.Maintenance = &maintenance.Daemon{}
	}
	config.dnsDaemonInit = new(sync.Once)
	if config.DNSDaemon == nil {
		config.DNSDaemon = &dnsd.Daemon{}
	}
	config.httpDaemonInit = new(sync.Once)
	if config.HTTPDaemon == nil {
		config.HTTPDaemon = &httpd.Daemon{}
	}
	config.mailDaemonInit = new(sync.Once)
	if config.MailDaemon == nil {
		config.MailDaemon = &smtpd.Daemon{}
	}
	config.plainSocketDaemonInit = new(sync.Once)
	if config.PlainSocketDaemon == nil {
		config.PlainSocketDaemon = &plainsocket.Daemon{}
	}
	config.simpleIPSvcDaemonInit = new(sync.Once)
	if config.SimpleIPSvcDaemon == nil {
		config.SimpleIPSvcDaemon = &simpleipsvcd.Daemon{}
	}
	config.snmpDaemonInit = new(sync.Once)
	if config.SNMPDaemon == nil {
		config.SNMPDaemon = &snmpd.Daemon{}
	}
	config.sockDaemonInit = new(sync.Once)
	if config.SockDaemon == nil {
		config.SockDaemon = &sockd.Daemon{}
	}
	config.telegramBotInit = new(sync.Once)
	if config.TelegramBot == nil {
		config.TelegramBot = &telegrambot.Daemon{}
	}
	config.autoUnlockInit = new(sync.Once)
	if config.AutoUnlock == nil {
		config.AutoUnlock = &autounlock.Daemon{}
	}
	// All notification filters share the common mail client
	config.DNSFilters.NotifyViaEmail.MailClient = config.MailClient
	config.HTTPFilters.NotifyViaEmail.MailClient = config.MailClient
	config.MailFilters.NotifyViaEmail.MailClient = config.MailClient
{'fraction_non_alphanumeric': 0.08374221291140636, 'fraction_numerical': 0.0013231159380340703, 'mean_word_length': 4.498635950287966, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6494585', 'n_tokens_mistral': 5474, 'n_tokens_neox': 5353, 'n_words': 1265}