package main import ( "fmt" "io/fs" "net/url" "os" "path/filepath" "strconv" "strings" "time" "github.com/hrfee/jfa-go/easyproxy" lm "github.com/hrfee/jfa-go/logmessages" "gopkg.in/ini.v1" ) var emailEnabled = false var messagesEnabled = false var telegramEnabled = false var discordEnabled = false var matrixEnabled = false func (app *appContext) GetPath(sect, key string) (fs.FS, string) { val := app.config.Section(sect).Key(key).MustString("") if strings.HasPrefix(val, "jfa-go:") { return localFS, strings.TrimPrefix(val, "jfa-go:") } dir, file := filepath.Split(val) return os.DirFS(dir), file } func (app *appContext) MustSetValue(section, key, val string) { app.config.Section(section).Key(key).SetValue(app.config.Section(section).Key(key).MustString(val)) } func (app *appContext) loadConfig() error { var err error app.config, err = ini.Load(app.configPath) if err != nil { return err } app.MustSetValue("jellyfin", "public_server", app.config.Section("jellyfin").Key("server").String()) app.MustSetValue("ui", "redirect_url", app.config.Section("jellyfin").Key("public_server").String()) for _, key := range app.config.Section("files").Keys() { if name := key.Name(); name != "html_templates" && name != "lang_files" { key.SetValue(key.MustString(filepath.Join(app.dataPath, (key.Name() + ".json")))) } } for _, key := range []string{"user_configuration", "user_displayprefs", "user_profiles", "ombi_template", "invites", "emails", "user_template", "custom_emails", "users", "telegram_users", "discord_users", "matrix_users", "announcements", "custom_user_page_content"} { app.config.Section("files").Key(key).SetValue(app.config.Section("files").Key(key).MustString(filepath.Join(app.dataPath, (key + ".json")))) } for _, key := range []string{"matrix_sql"} { app.config.Section("files").Key(key).SetValue(app.config.Section("files").Key(key).MustString(filepath.Join(app.dataPath, (key + ".db")))) } app.URLBase = strings.TrimSuffix(app.config.Section("ui").Key("url_base").MustString(""), "/") if app.URLBase == "/invite" || app.URLBase == "/accounts" || app.URLBase == "/settings" || app.URLBase == "/activity" { app.err.Printf(lm.BadURLBase, app.URLBase) } app.ExternalURI = strings.TrimSuffix(strings.TrimSuffix(app.config.Section("ui").Key("jfa_url").MustString(""), "/invite"), "/") if !strings.HasSuffix(app.ExternalURI, app.URLBase) { app.err.Println(lm.NoURLSuffix) } if app.ExternalURI == "" { app.err.Println(lm.NoExternalHost + lm.LoginWontSave) } u, err := url.Parse(app.ExternalURI) if err == nil { app.ExternalDomain = u.Hostname() } app.config.Section("email").Key("no_username").SetValue(strconv.FormatBool(app.config.Section("email").Key("no_username").MustBool(false))) app.MustSetValue("password_resets", "email_html", "jfa-go:"+"email.html") app.MustSetValue("password_resets", "email_text", "jfa-go:"+"email.txt") app.MustSetValue("invite_emails", "email_html", "jfa-go:"+"invite-email.html") app.MustSetValue("invite_emails", "email_text", "jfa-go:"+"invite-email.txt") app.MustSetValue("email_confirmation", "email_html", "jfa-go:"+"confirmation.html") app.MustSetValue("email_confirmation", "email_text", "jfa-go:"+"confirmation.txt") app.MustSetValue("notifications", "expiry_html", "jfa-go:"+"expired.html") app.MustSetValue("notifications", "expiry_text", "jfa-go:"+"expired.txt") app.MustSetValue("notifications", "created_html", "jfa-go:"+"created.html") app.MustSetValue("notifications", "created_text", "jfa-go:"+"created.txt") app.MustSetValue("deletion", "email_html", "jfa-go:"+"deleted.html") app.MustSetValue("deletion", "email_text", "jfa-go:"+"deleted.txt") app.MustSetValue("smtp", "hello_hostname", "localhost") app.MustSetValue("smtp", "cert_validation", "true") app.MustSetValue("smtp", "auth_type", "4") app.MustSetValue("activity_log", "keep_n_records", "1000") app.MustSetValue("activity_log", "delete_after_days", "90") sc := app.config.Section("discord").Key("start_command").MustString("start") app.config.Section("discord").Key("start_command").SetValue(strings.TrimPrefix(strings.TrimPrefix(sc, "/"), "!")) jfUrl := app.config.Section("jellyfin").Key("server").String() if !(strings.HasPrefix(jfUrl, "http://") || strings.HasPrefix(jfUrl, "https://")) { app.config.Section("jellyfin").Key("server").SetValue("http://" + jfUrl) } // Deletion template is good enough for these as well. app.MustSetValue("disable_enable", "disabled_html", "jfa-go:"+"deleted.html") app.MustSetValue("disable_enable", "disabled_text", "jfa-go:"+"deleted.txt") app.MustSetValue("disable_enable", "enabled_html", "jfa-go:"+"deleted.html") app.MustSetValue("disable_enable", "enabled_text", "jfa-go:"+"deleted.txt") app.MustSetValue("welcome_email", "email_html", "jfa-go:"+"welcome.html") app.MustSetValue("welcome_email", "email_text", "jfa-go:"+"welcome.txt") app.MustSetValue("template_email", "email_html", "jfa-go:"+"template.html") app.MustSetValue("template_email", "email_text", "jfa-go:"+"template.txt") app.MustSetValue("user_expiry", "behaviour", "disable_user") app.MustSetValue("user_expiry", "email_html", "jfa-go:"+"user-expired.html") app.MustSetValue("user_expiry", "email_text", "jfa-go:"+"user-expired.txt") app.MustSetValue("user_expiry", "adjustment_email_html", "jfa-go:"+"expiry-adjusted.html") app.MustSetValue("user_expiry", "adjustment_email_text", "jfa-go:"+"expiry-adjusted.txt") app.MustSetValue("matrix", "topic", "Jellyfin notifications") app.MustSetValue("matrix", "show_on_reg", "true") app.MustSetValue("discord", "show_on_reg", "true") app.MustSetValue("telegram", "show_on_reg", "true") app.MustSetValue("backups", "every_n_minutes", "1440") app.MustSetValue("backups", "path", filepath.Join(app.dataPath, "backups")) app.MustSetValue("backups", "keep_n_backups", "20") app.config.Section("jellyfin").Key("version").SetValue(version) app.config.Section("jellyfin").Key("device").SetValue("jfa-go") app.config.Section("jellyfin").Key("device_id").SetValue(fmt.Sprintf("jfa-go-%s-%s", version, commit)) LOGIP = app.config.Section("advanced").Key("log_ips").MustBool(false) LOGIPU = app.config.Section("advanced").Key("log_ips_users").MustBool(false) pwrMethods := []string{"allow_pwr_username", "allow_pwr_email", "allow_pwr_contact_method"} allDisabled := true for _, v := range pwrMethods { if app.config.Section("user_page").Key(v).MustBool(true) { allDisabled = false } } if allDisabled { app.info.Println(lm.EnableAllPWRMethods) for _, v := range pwrMethods { app.config.Section("user_page").Key(v).SetValue("true") } } messagesEnabled = app.config.Section("messages").Key("enabled").MustBool(false) telegramEnabled = app.config.Section("telegram").Key("enabled").MustBool(false) discordEnabled = app.config.Section("discord").Key("enabled").MustBool(false) matrixEnabled = app.config.Section("matrix").Key("enabled").MustBool(false) if !messagesEnabled { emailEnabled = false telegramEnabled = false discordEnabled = false matrixEnabled = false } else if app.config.Section("email").Key("method").MustString("") == "" { emailEnabled = false } else { emailEnabled = true } if !emailEnabled && !telegramEnabled && !discordEnabled && !matrixEnabled { messagesEnabled = false } if app.proxyEnabled = app.config.Section("advanced").Key("proxy").MustBool(false); app.proxyEnabled { app.proxyConfig = easyproxy.ProxyConfig{} app.proxyConfig.Protocol = easyproxy.HTTP if strings.Contains(app.config.Section("advanced").Key("proxy_protocol").MustString("http"), "socks") { app.proxyConfig.Protocol = easyproxy.SOCKS5 } app.proxyConfig.Addr = app.config.Section("advanced").Key("proxy_address").MustString("") app.proxyConfig.User = app.config.Section("advanced").Key("proxy_user").MustString("") app.proxyConfig.Password = app.config.Section("advanced").Key("proxy_password").MustString("") app.proxyTransport, err = easyproxy.NewTransport(app.proxyConfig) if err != nil { app.err.Printf(lm.FailedInitProxy, app.proxyConfig.Addr, err) // As explained in lm.FailedInitProxy, sleep here might grab the admin's attention, // Since we don't crash on this failing. time.Sleep(15 * time.Second) app.proxyEnabled = false } else { app.proxyEnabled = true app.info.Printf(lm.InitProxy, app.proxyConfig.Addr) } } app.MustSetValue("updates", "enabled", "true") releaseChannel := app.config.Section("updates").Key("channel").String() if app.config.Section("updates").Key("enabled").MustBool(false) { v := version if releaseChannel == "stable" { if version == "git" { v = "0.0.0" } } else if releaseChannel == "unstable" { v = "git" } app.updater = newUpdater(baseURL, namespace, repo, v, commit, updater) if app.proxyEnabled { app.updater.SetTransport(app.proxyTransport) } } if releaseChannel == "" { if version == "git" { releaseChannel = "unstable" } else { releaseChannel = "stable" } app.MustSetValue("updates", "channel", releaseChannel) } substituteStrings = app.config.Section("jellyfin").Key("substitute_jellyfin_strings").MustString("") if substituteStrings != "" { v := app.config.Section("ui").Key("success_message") v.SetValue(strings.ReplaceAll(v.String(), "Jellyfin", substituteStrings)) } oldFormLang := app.config.Section("ui").Key("language").MustString("") if oldFormLang != "" { app.storage.lang.chosenUserLang = oldFormLang } newFormLang := app.config.Section("ui").Key("language-form").MustString("") if newFormLang != "" { app.storage.lang.chosenUserLang = newFormLang } app.storage.lang.chosenAdminLang = app.config.Section("ui").Key("language-admin").MustString("en-us") app.storage.lang.chosenEmailLang = app.config.Section("email").Key("language").MustString("en-us") app.storage.lang.chosenPWRLang = app.config.Section("password_resets").Key("language").MustString("en-us") app.storage.lang.chosenTelegramLang = app.config.Section("telegram").Key("language").MustString("en-us") app.email = NewEmailer(app) return nil }