@ -9,195 +9,167 @@ import (
"strings"
)
type Config struct {
type Sys Config struct {
Port int
Hostname string
CertPath string
KeyPath string
DocBase string
HomeDocBase string
GeminiExt string
DefaultLang string
DefaultEncoding string
AccessLog string
ErrorLog string
ReadMollyFiles bool
TempRedirects map [ string ] string
PermRedirects map [ string ] string
MimeOverrides map [ string ] string
DocBase string
HomeDocBase string
CGIPaths [ ] string
SCGIPaths map [ string ] string
CertificateZones map [ string ] [ ] string
ReadMollyFiles bool
AllowTLS12 bool
DirectorySort string
DirectorySubdirsFirst bool
DirectoryReverse bool
DirectoryTitles bool
}
type MollyFile struct {
type UserConfig struct {
GeminiExt string
DefaultLang string
DefaultEncoding string
TempRedirects map [ string ] string
PermRedirects map [ string ] string
MimeOverrides map [ string ] string
CertificateZones map [ string ] [ ] string
DefaultLang string
DefaultEncoding string
DirectorySort string
DirectorySubdirsFirst bool
DirectoryReverse bool
DirectoryTitles bool
}
func getConfig ( filename string ) ( Config, error ) {
func getConfig ( filename string ) ( SysConfig, User Config, error ) {
var config Config
var sysConfig SysConfig
var userConfig UserConfig
// Defaults
config . Port = 1965
config . Hostname = "localhost"
config . CertPath = "cert.pem"
config . KeyPath = "key.pem"
config . DocBase = "/var/gemini/"
config . HomeDocBase = "users"
config . GeminiExt = "gmi"
config . DefaultLang = ""
config . DefaultEncoding = ""
config . AccessLog = "access.log"
config . ErrorLog = ""
config . TempRedirects = make ( map [ string ] string )
config . PermRedirects = make ( map [ string ] string )
config . CGIPaths = make ( [ ] string , 0 )
config . SCGIPaths = make ( map [ string ] string )
config . AllowTLS12 = true
config . DirectorySort = "Name"
config . DirectorySubdirsFirst = false
sysConfig . Port = 1965
sysConfig . Hostname = "localhost"
sysConfig . CertPath = "cert.pem"
sysConfig . KeyPath = "key.pem"
sysConfig . AccessLog = "access.log"
sysConfig . ErrorLog = ""
sysConfig . DocBase = "/var/gemini/"
sysConfig . HomeDocBase = "users"
sysConfig . CGIPaths = make ( [ ] string , 0 )
sysConfig . SCGIPaths = make ( map [ string ] string )
sysConfig . ReadMollyFiles = false
sysConfig . AllowTLS12 = true
userConfig . GeminiExt = "gmi"
userConfig . DefaultLang = ""
userConfig . DefaultEncoding = ""
userConfig . TempRedirects = make ( map [ string ] string )
userConfig . PermRedirects = make ( map [ string ] string )
userConfig . DirectorySort = "Name"
userConfig . DirectorySubdirsFirst = false
// Return defaults if no filename given
if filename == "" {
return c onfig, nil
return sysConfig, userC onfig, nil
}
// Attempt to overwrite defaults from file
_ , err := toml . DecodeFile ( filename , & c onfig)
_ , err := toml . DecodeFile ( filename , & sysC onfig)
if err != nil {
return config , err
return sysConfig , userConfig , err
}
_ , err = toml . DecodeFile ( filename , & userConfig )
if err != nil {
return sysConfig , userConfig , err
}
// Force hostname to lowercase
config . Hostname = strings . ToLower ( config . Hostname )
sysConfig. Hostname = strings . ToLower ( sysC onfig. Hostname )
// Validate pseudo-enums
switch c onfig. DirectorySort {
case "Name" , "Size" , "Time" :
default :
return c onfig, errors . New ( "Invalid DirectorySort value." )
switch userC onfig. DirectorySort {
case "Name" , "Size" , "Time" :
default :
return sysConfig , userC onfig, errors . New ( "Invalid DirectorySort value." )
}
// Absolutise paths
c onfig. DocBase , err = filepath . Abs ( c onfig. DocBase )
sysC onfig. DocBase , err = filepath . Abs ( sysC onfig. DocBase )
if err != nil {
return c onfig, err
return sysConfig, userC onfig, err
}
c onfig. CertPath , err = filepath . Abs ( c onfig. CertPath )
sysC onfig. CertPath , err = filepath . Abs ( sysC onfig. CertPath )
if err != nil {
return c onfig, err
return sysConfig, userC onfig, err
}
c onfig. KeyPath , err = filepath . Abs ( c onfig. KeyPath )
sysC onfig. KeyPath , err = filepath . Abs ( sysC onfig. KeyPath )
if err != nil {
return c onfig, err
return sysConfig, userC onfig, err
}
if config. AccessLog != "" && c onfig. AccessLog != "-" {
c onfig. AccessLog , err = filepath . Abs ( c onfig. AccessLog )
if sysConfig. AccessLog != "" && sysC onfig. AccessLog != "-" {
sysC onfig. AccessLog , err = filepath . Abs ( sysC onfig. AccessLog )
if err != nil {
return c onfig, err
return sysConfig, userC onfig, err
}
}
if c onfig. ErrorLog != "" {
c onfig. ErrorLog , err = filepath . Abs ( c onfig. ErrorLog )
if sysC onfig. ErrorLog != "" {
sysC onfig. ErrorLog , err = filepath . Abs ( sysC onfig. ErrorLog )
if err != nil {
return c onfig, err
return sysConfig, userC onfig, err
}
}
// Absolutise CGI paths
for index , cgiPath := range c onfig. CGIPaths {
for index , cgiPath := range sysC onfig. CGIPaths {
if ! filepath . IsAbs ( cgiPath ) {
c onfig. CGIPaths [ index ] = filepath . Join ( c onfig. DocBase , cgiPath )
sysC onfig. CGIPaths [ index ] = filepath . Join ( sysC onfig. DocBase , cgiPath )
}
}
// Expand CGI paths
var cgiPaths [ ] string
for _ , cgiPath := range c onfig. CGIPaths {
for _ , cgiPath := range sysC onfig. CGIPaths {
expandedPaths , err := filepath . Glob ( cgiPath )
if err != nil {
return c onfig, errors . New ( "Error expanding CGI path glob " + cgiPath + ": " + err . Error ( ) )
return sysConfig, userC onfig, errors . New ( "Error expanding CGI path glob " + cgiPath + ": " + err . Error ( ) )
}
cgiPaths = append ( cgiPaths , expandedPaths ... )
}
c onfig. CGIPaths = cgiPaths
sysC onfig. CGIPaths = cgiPaths
// Absolutise SCGI paths
for index , scgiPath := range c onfig. SCGIPaths {
c onfig. SCGIPaths [ index ] , err = filepath . Abs ( scgiPath )
for index , scgiPath := range sysC onfig. SCGIPaths {
sysC onfig. SCGIPaths [ index ] , err = filepath . Abs ( scgiPath )
if err != nil {
return c onfig, err
return sysConfig, userC onfig, err
}
}
// Validate redirects
for _ , value := range c onfig. TempRedirects {
for _ , value := range userC onfig. TempRedirects {
if strings . Contains ( value , "://" ) && ! strings . HasPrefix ( value , "gemini://" ) {
return c onfig, errors . New ( "Invalid cross-protocol redirect to " + value )
return sysConfig, userC onfig, errors . New ( "Invalid cross-protocol redirect to " + value )
}
}
for _ , value := range c onfig. PermRedirects {
for _ , value := range userC onfig. PermRedirects {
if strings . Contains ( value , "://" ) && ! strings . HasPrefix ( value , "gemini://" ) {
return c onfig, errors . New ( "Ignoring cross-protocol redirect to " + value )
return sysConfig, userC onfig, errors . New ( "Ignoring cross-protocol redirect to " + value )
}
}
return c onfig, nil
return sysConfig, userC onfig, nil
}
func parseMollyFiles ( path string , config * Config) {
func parseMollyFiles ( path string , docBase string , config User Config) UserConfig {
// Replace config variables which use pointers with new ones,
// so that changes made here aren't reflected everywhere.
newTempRedirects := make ( map [ string ] string )
for key , value := range config . TempRedirects {
newTempRedirects [ key ] = value
}
config . TempRedirects = newTempRedirects
newPermRedirects := make ( map [ string ] string )
for key , value := range config . PermRedirects {
newPermRedirects [ key ] = value
}
config . PermRedirects = newPermRedirects
newMimeOverrides := make ( map [ string ] string )
for key , value := range config . MimeOverrides {
newMimeOverrides [ key ] = value
}
config . MimeOverrides = newMimeOverrides
newCertificateZones := make ( map [ string ] [ ] string )
for key , value := range config . CertificateZones {
newCertificateZones [ key ] = value
}
config . CertificateZones = newCertificateZones
// Initialise MollyFile using main Config
var mollyFile MollyFile
mollyFile . GeminiExt = config . GeminiExt
mollyFile . DefaultLang = config . DefaultLang
mollyFile . DefaultEncoding = config . DefaultEncoding
mollyFile . DirectorySort = config . DirectorySort
mollyFile . DirectorySubdirsFirst = config . DirectorySubdirsFirst
mollyFile . DirectoryReverse = config . DirectoryReverse
mollyFile . DirectoryTitles = config . DirectoryTitles
config . TempRedirects = make ( map [ string ] string )
config . PermRedirects = make ( map [ string ] string )
config . MimeOverrides = make ( map [ string ] string )
config . CertificateZones = make ( map [ string ] [ ] string )
// Build list of directories to check
var dirs [ ] string
dirs = append ( dirs , path )
for {
if path == filepath . Clean ( config. D ocBase) {
if path == filepath . Clean ( docBase ) {
break
}
subpath := filepath . Dir ( path )
@ -219,38 +191,28 @@ func parseMollyFiles(path string, config *Config) {
continue
}
// If the file exists and we can read it, try to parse it
_ , err = toml . DecodeFile ( mollyPath , & mollyFile )
_ , err = toml . DecodeFile ( mollyPath , & config )
if err != nil {
log . Println ( "Error parsing .molly file " + mollyPath + ": " + err . Error ( ) )
continue
}
// Overwrite main Config using MollyFile
config . GeminiExt = mollyFile . GeminiExt
config . DefaultLang = mollyFile . DefaultLang
config . DefaultEncoding = mollyFile . DefaultEncoding
config . DirectorySort = mollyFile . DirectorySort
config . DirectorySubdirsFirst = mollyFile . DirectorySubdirsFirst
config . DirectoryReverse = mollyFile . DirectoryReverse
config . DirectoryTitles = mollyFile . DirectoryTitles
for key , value := range mollyFile . TempRedirects {
for key , value := range config . TempRedirects {
if strings . Contains ( value , "://" ) && ! strings . HasPrefix ( value , "gemini://" ) {
log . Println ( "Ignoring cross-protocol redirect to " + value + " in .molly file " + mollyPath )
continue
}
config . TempRedirects [ key ] = value
}
for key , value := range mollyFile . PermRedirects {
for key , value := range config . PermRedirects {
if strings . Contains ( value , "://" ) && ! strings . HasPrefix ( value , "gemini://" ) {
log . Println ( "Ignoring cross-protocol redirect to " + value + " in .molly file " + mollyPath )
continue
}
config . PermRedirects [ key ] = value
}
for key , value := range mollyFile . MimeOverrides {
config . MimeOverrides [ key ] = value
}
for key , value := range mollyFile . CertificateZones {
config . CertificateZones [ key ] = value
}
}
return config
}