// apparmor.d - Full set of apparmor profiles
// Copyright (C) 2021-2024 Alexandre Pujol <alexandre@pujol.io>
// SPDX-License-Identifier: GPL-2.0-only

package cfg

import (
	"strings"
)

// Filter out comments from a text configuration file
func filterComment(line string) (string, bool) {
	if strings.HasPrefix(line, "#") || line == "" {
		return "", true
	}
	if strings.Contains(line, "#") {
		line = strings.Split(line, "#")[0]
		line = strings.TrimSpace(line)
		if line == "" {
			return "", true
		}
	}
	return line, false
}

type Flagger struct{}

func (f Flagger) Read(name string) map[string][]string {
	res := map[string][]string{}
	path := FlagDir.Join(name + ".flags")
	if !path.Exist() {
		return res
	}

	lines, _ := path.ReadFileAsLines()
	for _, line := range lines {
		line, next := filterComment(line)
		if next {
			continue
		}
		manifest := strings.Split(line, " ")
		profile := manifest[0]
		flags := []string{}
		if len(manifest) > 1 {
			flags = strings.Split(manifest[1], ",")
		}
		res[profile] = flags
	}
	return res
}

type Ignorer struct{}

func (i Ignorer) Read(name string) []string {
	res := []string{}
	path := IgnoreDir.Join(name + ".ignore")
	if !path.Exist() {
		return res
	}

	lines, _ := path.ReadFileAsLines()
	for _, line := range lines {
		line, next := filterComment(line)
		if next {
			continue
		}
		res = append(res, line)
	}
	return res
}

type Overwriter struct {
	Enabled bool
}

// Get the list of upstream profiles to overwrite from dist/overwrite
func (o Overwriter) Get() []string {
	res := []string{}
	lines, err := DistDir.Join("overwrite").ReadFileAsLines()
	if err != nil {
		panic(err)
	}
	for _, line := range lines {
		line, next := filterComment(line)
		if next {
			continue
		}
		res = append(res, line)
	}
	return res
}

// Overwrite upstream profile for APT: rename our profile & hide upstream
func (o Overwriter) Apt(files []string) {
	const ext = ".apparmor.d"
	file, err := DebianDir.Join("apparmor.d.hide").Append()
	if err != nil {
		panic(err)
	}
	for _, name := range files {
		origin := RootApparmord.Join(name)
		dest := RootApparmord.Join(name + ext)
		if err := origin.Rename(dest); err != nil {
			panic(err)
		}
		if _, err := file.WriteString("/etc/apparmor.d/" + name + "\n"); err != nil {
			panic(err)
		}
	}
}

// Clean the debian/apparmor.d.hide file
func (o Overwriter) AptClean() {
	const debianHide = `# This file is generated by "make", all edit will be lost.

/etc/apparmor.d/usr.bin.firefox
/etc/apparmor.d/usr.sbin.cups-browsed
/etc/apparmor.d/usr.sbin.cupsd
/etc/apparmor.d/usr.sbin.rsyslogd
`
	path := DebianDir.Join("apparmor.d.hide")
	if err := path.WriteFile([]byte(debianHide)); err != nil {
		panic(err)
	}
}