440 lines
10 KiB
Go
440 lines
10 KiB
Go
package sql
|
|
|
|
import (
|
|
"database/sql"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
|
|
"github.com/filebrowser/filebrowser/v2/auth"
|
|
"github.com/filebrowser/filebrowser/v2/files"
|
|
"github.com/filebrowser/filebrowser/v2/rules"
|
|
"github.com/filebrowser/filebrowser/v2/settings"
|
|
"github.com/filebrowser/filebrowser/v2/users"
|
|
)
|
|
|
|
func init() {
|
|
}
|
|
|
|
type settingsBackend struct {
|
|
db *sql.DB
|
|
}
|
|
|
|
func InitSettingsTable(db *sql.DB) error {
|
|
sql := "create table if not exists settings(key string primary key, value string)"
|
|
_, err := db.Exec(sql)
|
|
checkError(err, "Fail to create table settings")
|
|
return err
|
|
}
|
|
|
|
func bytesToString(data []byte) string {
|
|
return base64.RawStdEncoding.EncodeToString(data)
|
|
}
|
|
|
|
func bytesFromString(s string) ([]byte, error) {
|
|
return base64.RawStdEncoding.DecodeString(s)
|
|
}
|
|
|
|
func userDefaultsFromString(s string) settings.UserDefaults {
|
|
if s == "" {
|
|
return settings.UserDefaults{}
|
|
}
|
|
userDefaults := settings.UserDefaults{}
|
|
err := json.Unmarshal([]byte(s), &userDefaults)
|
|
checkError(err, "Fail to parse settings.UserDefaults")
|
|
return userDefaults
|
|
}
|
|
|
|
func userDefaultsToString(d settings.UserDefaults) string {
|
|
data, err := json.Marshal(d)
|
|
if checkError(err, "Fail to stringify settings.UserDefaults") {
|
|
return ""
|
|
}
|
|
return string(data)
|
|
}
|
|
|
|
func brandingFromString(s string) settings.Branding {
|
|
if s == "" {
|
|
return settings.Branding{}
|
|
}
|
|
branding := settings.Branding{}
|
|
err := json.Unmarshal([]byte(s), &branding)
|
|
checkError(err, "Fail to parse settings.Branding")
|
|
return branding
|
|
}
|
|
|
|
func brandingToString(s settings.Branding) string {
|
|
data, err := json.Marshal(s)
|
|
if checkError(err, "Fail to jsonify settings.Branding") {
|
|
return ""
|
|
}
|
|
return string(data)
|
|
}
|
|
|
|
func commandsToString(c map[string][]string) string {
|
|
data, err := json.Marshal(c)
|
|
if checkError(err, "Fail to jsonify commands") {
|
|
return ""
|
|
}
|
|
return string(data)
|
|
}
|
|
|
|
func commandsFromString(s string) map[string][]string {
|
|
c := make(map[string][]string)
|
|
if s == "" {
|
|
return c
|
|
}
|
|
err := json.Unmarshal([]byte(s), &c)
|
|
checkError(err, "Fail to parse commands")
|
|
return c
|
|
}
|
|
|
|
func stringsFromString(s string) []string {
|
|
c := make([]string, 0)
|
|
if s == "" {
|
|
return c
|
|
}
|
|
err := json.Unmarshal([]byte(s), &c)
|
|
checkError(err, "Fail to parse []string")
|
|
return c
|
|
}
|
|
|
|
func stringsToString(c []string) string {
|
|
data, err := json.Marshal(c)
|
|
if checkError(err, "Fail to jsonify strings") {
|
|
return ""
|
|
}
|
|
return string(data)
|
|
}
|
|
|
|
func boolToInt(b bool) int {
|
|
if b {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func boolFromInt(i int) bool {
|
|
if i == 0 {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func boolFromString(s string) bool {
|
|
if s == "0" || s == "" || s == "f" || s == "F" {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func boolToString(b bool) string {
|
|
if b {
|
|
return "1"
|
|
}
|
|
return "0"
|
|
}
|
|
|
|
func (s settingsBackend) Get() (*settings.Settings, error) {
|
|
sql := "select key, value from settings"
|
|
rows, err := s.db.Query(sql)
|
|
if checkError(err, "Fail to Query settings.Settings") {
|
|
return nil, err
|
|
}
|
|
key := ""
|
|
value := ""
|
|
settings1 := cloneSettings(defaultSettings)
|
|
for rows.Next() {
|
|
err = rows.Scan(&key, &value)
|
|
checkError(err, "Fail to query settings.Settings")
|
|
if key == "Key" {
|
|
val, err := bytesFromString(value)
|
|
if !checkError(err, "Fail to parse []byte from string") {
|
|
settings1.Key = val
|
|
}
|
|
} else if key == "Signup" {
|
|
settings1.Signup = boolFromString(value)
|
|
} else if key == "CreateUserDir" {
|
|
settings1.CreateUserDir = boolFromString(value)
|
|
} else if key == "UserHomeBasePath" {
|
|
settings1.UserHomeBasePath = value
|
|
} else if key == "Defaults" {
|
|
settings1.Defaults = userDefaultsFromString(value)
|
|
} else if key == "AuthMethod" {
|
|
settings1.AuthMethod = settings.AuthMethod(value)
|
|
} else if key == "Branding" {
|
|
settings1.Branding = brandingFromString(value)
|
|
} else if key == "Commands" {
|
|
settings1.Commands = commandsFromString(value)
|
|
} else if key == "Shell" {
|
|
settings1.Shell = stringsFromString(value)
|
|
} else if key == "Rules" {
|
|
settings1.Rules = rulesFromString(value)
|
|
}
|
|
}
|
|
if len(settings1.Key) == 0 {
|
|
return nil, nil
|
|
}
|
|
return &settings1, nil
|
|
}
|
|
|
|
func (s settingsBackend) Save(ss *settings.Settings) error {
|
|
fields := []string{"Key", "Signup", "CreateUserDir", "UserHomeBasePath", "Defaults", "AuthMethod", "Branding", "Commands", "Shell", "Rules"}
|
|
values := []string{
|
|
bytesToString(ss.Key),
|
|
boolToString(ss.Signup),
|
|
boolToString(ss.CreateUserDir),
|
|
ss.UserHomeBasePath,
|
|
userDefaultsToString(ss.Defaults),
|
|
string(ss.AuthMethod),
|
|
brandingToString(ss.Branding),
|
|
commandsToString(ss.Commands),
|
|
stringsToString(ss.Shell),
|
|
RulesToString(ss.Rules),
|
|
}
|
|
tx, err := s.db.Begin()
|
|
if checkError(err, "Fail to begin db transaction") {
|
|
return err
|
|
}
|
|
for i, field := range fields {
|
|
exists := ContainKey(s.db, field)
|
|
sql := "INSERT INTO settings (value, key) VALUES(?,?)"
|
|
if exists {
|
|
sql = "UPDATE settings set value = ? where key = ?"
|
|
}
|
|
stmt, err := s.db.Prepare(sql)
|
|
defer stmt.Close()
|
|
if checkError(err, "Fail to prepare statement") {
|
|
tx.Rollback()
|
|
break
|
|
}
|
|
_, err = stmt.Exec(values[i], field)
|
|
if checkError(err, "Fail to insert field "+field+" of settings") {
|
|
tx.Rollback()
|
|
break
|
|
}
|
|
}
|
|
err = tx.Commit()
|
|
if checkError(err, "Fail to commit") {
|
|
tx.Rollback()
|
|
return err
|
|
}
|
|
return err
|
|
}
|
|
|
|
var defaultServer = settings.Server{
|
|
Port: "8080",
|
|
Log: "stdout",
|
|
EnableThumbnails: false,
|
|
ResizePreview: false,
|
|
EnableExec: false,
|
|
TypeDetectionByHeader: false,
|
|
}
|
|
|
|
var defaultSettings = settings.Settings{
|
|
Key: []byte(""),
|
|
Signup: false,
|
|
CreateUserDir: false,
|
|
UserHomeBasePath: "/users",
|
|
Defaults: settings.UserDefaults{
|
|
Scope: ".",
|
|
Locale: "en",
|
|
ViewMode: "mosaic",
|
|
SingleClick: false,
|
|
Sorting: files.Sorting{
|
|
By: "",
|
|
Asc: false,
|
|
},
|
|
Perm: users.Permissions{
|
|
Admin: false,
|
|
Execute: true,
|
|
Create: true,
|
|
Rename: true,
|
|
Modify: true,
|
|
Delete: true,
|
|
Share: true,
|
|
Download: true,
|
|
},
|
|
Commands: make([]string, 0),
|
|
HideDotfiles: false,
|
|
DateFormat: false,
|
|
},
|
|
AuthMethod: auth.MethodJSONAuth,
|
|
Branding: settings.Branding{
|
|
Name: "",
|
|
DisableExternal: false,
|
|
Files: "",
|
|
Theme: "",
|
|
Color: "",
|
|
},
|
|
Commands: make(map[string][]string),
|
|
Shell: make([]string, 0),
|
|
Rules: make([]rules.Rule, 0),
|
|
}
|
|
|
|
func cloneServer(server settings.Server) settings.Server {
|
|
data, err := json.Marshal(server)
|
|
s := settings.Server{}
|
|
if checkError(err, "Fail to clone settings.Server") {
|
|
return s
|
|
}
|
|
err = json.Unmarshal(data, &s)
|
|
checkError(err, "Fail to decode for settings.Server")
|
|
return s
|
|
}
|
|
|
|
func cloneSettings(s settings.Settings) settings.Settings {
|
|
data, err := json.Marshal(s)
|
|
s1 := settings.Settings{}
|
|
if checkError(err, "Fail to clone settings.Settings") {
|
|
return s1
|
|
}
|
|
json.Unmarshal(data, &s1)
|
|
return s1
|
|
}
|
|
|
|
func (s settingsBackend) GetServer() (*settings.Server, error) {
|
|
sql := "select key, value from settings"
|
|
rows, err := s.db.Query(sql)
|
|
if checkError(err, "Fail to Query for GetServer") {
|
|
return nil, err
|
|
}
|
|
server := cloneServer(defaultServer)
|
|
key := ""
|
|
value := ""
|
|
|
|
for rows.Next() {
|
|
err = rows.Scan(&key, &value)
|
|
if checkError(err, "Fail to query settings.Settings") {
|
|
continue
|
|
}
|
|
if key == "Root" {
|
|
server.Root = value
|
|
} else if key == "BaseURL" {
|
|
server.BaseURL = value
|
|
} else if key == "Socket" {
|
|
server.Socket = value
|
|
} else if key == "TLSKey" {
|
|
server.TLSKey = value
|
|
} else if key == "TLSCert" {
|
|
server.TLSCert = value
|
|
} else if key == "Port" {
|
|
server.Port = value
|
|
} else if key == "Address" {
|
|
server.Address = value
|
|
} else if key == "Log" {
|
|
server.Log = value
|
|
} else if key == "EnableThumbnails" {
|
|
server.EnableThumbnails = boolFromString(value)
|
|
} else if key == "ResizePreview" {
|
|
server.ResizePreview = boolFromString(value)
|
|
} else if key == "EnableExec" {
|
|
server.EnableExec = boolFromString(value)
|
|
} else if key == "TypeDetectionByHeader" {
|
|
server.TypeDetectionByHeader = boolFromString(value)
|
|
} else if key == "AuthHook" {
|
|
server.AuthHook = value
|
|
}
|
|
}
|
|
return &server, nil
|
|
}
|
|
|
|
func (s settingsBackend) SaveServer(ss *settings.Server) error {
|
|
fields := []string{"Root", "BaseURL", "Socket", "TLSKey", "TLSCert", "Port", "Address", "Log", "EnableThumbnails", "ResizePreview", "EnableExec", "TypeDetectionByHeader", "AuthHook"}
|
|
values := []string{
|
|
ss.Root,
|
|
ss.BaseURL,
|
|
ss.Socket,
|
|
ss.TLSKey,
|
|
ss.TLSCert,
|
|
ss.Port,
|
|
ss.Address,
|
|
ss.Log,
|
|
boolToString(ss.EnableThumbnails),
|
|
boolToString(ss.ResizePreview),
|
|
boolToString(ss.EnableExec),
|
|
boolToString(ss.TypeDetectionByHeader),
|
|
ss.AuthHook}
|
|
tx, err := s.db.Begin()
|
|
if checkError(err, "Fail to begin db transaction") {
|
|
return err
|
|
}
|
|
for i, field := range fields {
|
|
stmt, err := s.db.Prepare("INSERT INTO settings (key, value) VALUES(?,?)")
|
|
defer stmt.Close()
|
|
if checkError(err, "Fail to prepare statement") {
|
|
tx.Rollback()
|
|
break
|
|
}
|
|
_, err = stmt.Exec(field, values[i])
|
|
if checkError(err, "Fail to insert field "+field+" of settings") {
|
|
tx.Rollback()
|
|
break
|
|
}
|
|
}
|
|
err = tx.Commit()
|
|
if checkError(err, "Fail to commit") {
|
|
tx.Rollback()
|
|
return err
|
|
}
|
|
return err
|
|
}
|
|
|
|
func SetSetting(db *sql.DB, key string, value string) error {
|
|
sql := "select count(key) from settings where key = '" + key + "'"
|
|
count := 0
|
|
err := db.QueryRow(sql).Scan(&count)
|
|
if checkError(err, "Fail to QueryRow for key="+key) {
|
|
return err
|
|
}
|
|
if count == 0 {
|
|
return addSetting(db, key, value)
|
|
}
|
|
return updateSetting(db, key, value)
|
|
}
|
|
|
|
func GetSetting(db *sql.DB, key string) string {
|
|
sql := "select value from settings where key = '" + key + "';"
|
|
value := ""
|
|
err := db.QueryRow(sql).Scan(&value)
|
|
if checkError(err, "Fail to QueryRow for key "+key) {
|
|
return value
|
|
}
|
|
return value
|
|
}
|
|
|
|
func addSetting(db *sql.DB, key string, value string) error {
|
|
sql := "insert into settings(key, value) values('" + key + "', '" + value + "')"
|
|
_, err := db.Exec(sql)
|
|
checkError(err, "Fail to addSetting")
|
|
return err
|
|
}
|
|
|
|
func updateSetting(db *sql.DB, key string, value string) error {
|
|
sql := "update settings set value = '" + value + "' where key = '" + key + "'"
|
|
_, err := db.Exec(sql)
|
|
checkError(err, "Fail to updateSetting")
|
|
return err
|
|
}
|
|
|
|
func HadSetting(db *sql.DB) bool {
|
|
key := GetSetting(db, "Key")
|
|
if key == "" {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func ContainKey(db *sql.DB, key string) bool {
|
|
sql := "select value from settings where key = '" + key + "';"
|
|
value := ""
|
|
err := db.QueryRow(sql).Scan(&value)
|
|
if checkError(err, "Fail to QueryRow for key "+key) {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func HadSettingOfKey(db *sql.DB, key string) bool {
|
|
return GetSetting(db, "Key") == key
|
|
}
|