+
+
+
+
+
{{ if .IsDocker }}
@@ -122,6 +127,7 @@
+
@@ -616,6 +622,7 @@ function TestAWS(button) {
{{ end }}
document.getElementById("port").value = "{{ .Port }}";
document.getElementById("url").value = "{{ .Settings.ServerUrl }}";
+ document.getElementById("public_name").value = "{{ .Settings.PublicName }}";
document.getElementById("url_redirection").value = "{{ .Settings.RedirectUrl }}";
document.getElementById("authentication_sel").value = "{{ .Auth.Method }}";
authSelectionChanged("{{ .Auth.Method }}")
diff --git a/internal/models/Configuration.go b/internal/models/Configuration.go
index c8db8568..c0b2dd45 100644
--- a/internal/models/Configuration.go
+++ b/internal/models/Configuration.go
@@ -11,6 +11,7 @@ type Configuration struct {
Port string `json:"Port"`
ServerUrl string `json:"ServerUrl"`
RedirectUrl string `json:"RedirectUrl"`
+ PublicName string `json:"PublicName"`
ConfigVersion int `json:"ConfigVersion"`
LengthId int `json:"LengthId"`
DataDir string `json:"DataDir"`
diff --git a/internal/models/Configuration_test.go b/internal/models/Configuration_test.go
index 9bc4d260..af778707 100644
--- a/internal/models/Configuration_test.go
+++ b/internal/models/Configuration_test.go
@@ -23,12 +23,13 @@ var testConfig = Configuration{
Port: ":12345",
ServerUrl: "https://testserver.com/",
RedirectUrl: "https://test.com",
- ConfigVersion: 11,
+ ConfigVersion: 14,
LengthId: 5,
DataDir: "test",
MaxMemory: 50,
UseSsl: true,
MaxFileSizeMB: 20,
+ PublicName: "public-name",
Encryption: Encryption{
Level: 1,
Cipher: []byte{0x00},
@@ -47,4 +48,4 @@ func TestConfiguration_ToString(t *testing.T) {
test.IsEqualString(t, testConfig.ToString(), exptectedUnidentedOutput)
}
-const exptectedUnidentedOutput = `{"Authentication":{"Method":0,"SaltAdmin":"saltadmin","SaltFiles":"saltfiles","Username":"admin","Password":"adminpwhashed","HeaderKey":"","OauthProvider":"","OAuthClientId":"","OAuthClientSecret":"","HeaderUsers":null,"OauthUsers":null},"Port":":12345","ServerUrl":"https://testserver.com/","RedirectUrl":"https://test.com","ConfigVersion":11,"LengthId":5,"DataDir":"test","MaxMemory":50,"UseSsl":true,"MaxFileSizeMB":20,"Encryption":{"Level":1,"Cipher":"AA==","Salt":"encsalt","Checksum":"encsum","ChecksumSalt":"encsumsalt"},"PicturesAlwaysLocal":true}`
+const exptectedUnidentedOutput = `{"Authentication":{"Method":0,"SaltAdmin":"saltadmin","SaltFiles":"saltfiles","Username":"admin","Password":"adminpwhashed","HeaderKey":"","OauthProvider":"","OAuthClientId":"","OAuthClientSecret":"","HeaderUsers":null,"OauthUsers":null},"Port":":12345","ServerUrl":"https://testserver.com/","RedirectUrl":"https://test.com","PublicName":"public-name","ConfigVersion":14,"LengthId":5,"DataDir":"test","MaxMemory":50,"UseSsl":true,"MaxFileSizeMB":20,"Encryption":{"Level":1,"Cipher":"AA==","Salt":"encsalt","Checksum":"encsum","ChecksumSalt":"encsumsalt"},"PicturesAlwaysLocal":true}`
diff --git a/internal/webserver/Webserver.go b/internal/webserver/Webserver.go
index 607bc2a3..f9030f66 100644
--- a/internal/webserver/Webserver.go
+++ b/internal/webserver/Webserver.go
@@ -5,6 +5,7 @@ Handling of webserver and requests / uploads
*/
import (
+ "bytes"
"context"
"embed"
"encoding/base64"
@@ -35,6 +36,7 @@ import (
"os"
"sort"
"strings"
+ templatetext "text/template"
"time"
)
@@ -71,8 +73,6 @@ var templateFolder *template.Template
var imageExpiredPicture []byte
-const expiredFile = "static/expired.png"
-
var srv http.Server
var sseServer *sse.Server
@@ -90,13 +90,12 @@ func Start() {
if helper.FolderExists("static") {
fmt.Println("Found folder 'static', using local folder instead of internal static folder")
mux.Handle("/", http.FileServer(http.Dir("static")))
- imageExpiredPicture, err = os.ReadFile(expiredFile)
- helper.Check(err)
} else {
mux.Handle("/", http.FileServer(http.FS(webserverDir)))
- imageExpiredPicture, err = fs.ReadFile(staticFolderEmbedded, "web/"+expiredFile)
helper.Check(err)
}
+ loadExpiryImage()
+
mux.HandleFunc("/admin", requireLogin(showAdminMenu, false))
mux.HandleFunc("/api/", processApi)
mux.HandleFunc("/apiDelete", requireLogin(deleteApiKey, false))
@@ -157,6 +156,16 @@ func Start() {
}
}
+func loadExpiryImage() {
+ svgTemplate, err := templatetext.ParseFS(templateFolderEmbedded, "web/templates/expired_file_svg.tmpl")
+ helper.Check(err)
+ var buf bytes.Buffer
+ view := UploadView{}
+ err = svgTemplate.Execute(&buf, view.convertGlobalConfig(ViewMain))
+ helper.Check(err)
+ imageExpiredPicture = buf.Bytes()
+}
+
// Shutdown closes the webserver gracefully
func Shutdown() {
sseServer.Close()
@@ -211,7 +220,7 @@ func doLogout(w http.ResponseWriter, r *http.Request) {
// Handling of /index and redirecting to globalConfig.RedirectUrl
func showIndex(w http.ResponseWriter, r *http.Request) {
- err := templateFolder.ExecuteTemplate(w, "index", genericView{RedirectUrl: configuration.Get().RedirectUrl})
+ err := templateFolder.ExecuteTemplate(w, "index", genericView{RedirectUrl: configuration.Get().RedirectUrl, PublicName: configuration.Get().PublicName})
helper.Check(err)
}
@@ -228,19 +237,19 @@ func showError(w http.ResponseWriter, r *http.Request) {
if r.URL.Query().Has("key") {
errorReason = wrongCipher
}
- err := templateFolder.ExecuteTemplate(w, "error", genericView{ErrorId: errorReason})
+ err := templateFolder.ExecuteTemplate(w, "error", genericView{ErrorId: errorReason, PublicName: configuration.Get().PublicName})
helper.Check(err)
}
// Handling of /error-auth
func showErrorAuth(w http.ResponseWriter, r *http.Request) {
- err := templateFolder.ExecuteTemplate(w, "error_auth", genericView{})
+ err := templateFolder.ExecuteTemplate(w, "error_auth", genericView{PublicName: configuration.Get().PublicName})
helper.Check(err)
}
// Handling of /forgotpw
func forgotPassword(w http.ResponseWriter, r *http.Request) {
- err := templateFolder.ExecuteTemplate(w, "forgotpw", genericView{})
+ err := templateFolder.ExecuteTemplate(w, "forgotpw", genericView{PublicName: configuration.Get().PublicName})
helper.Check(err)
}
@@ -303,15 +312,18 @@ func showLogin(w http.ResponseWriter, r *http.Request) {
IsFailedLogin: failedLogin,
User: user,
IsAdminView: false,
+ PublicName: configuration.Get().PublicName,
})
helper.Check(err)
}
// LoginView contains variables for the login template
type LoginView struct {
- IsFailedLogin bool
- User string
- IsAdminView bool
+ IsFailedLogin bool
+ IsAdminView bool
+ IsDownloadView bool
+ User string
+ PublicName string
}
// Handling of /d
@@ -330,7 +342,9 @@ func showDownload(w http.ResponseWriter, r *http.Request) {
Name: file.Name,
Size: file.Size,
Id: file.Id,
+ IsDownloadView: true,
EndToEndEncryption: file.Encryption.IsEndToEndEncrypted,
+ PublicName: configuration.Get().PublicName,
IsFailedLogin: false,
UsesHttps: configuration.UsesHttps(),
}
@@ -354,6 +368,7 @@ func showDownload(w http.ResponseWriter, r *http.Request) {
case <-time.After(1 * time.Second):
}
}
+ view.IsPasswordView = true
err := templateFolder.ExecuteTemplate(w, "download_password", view)
helper.Check(err)
return
@@ -376,7 +391,7 @@ func showHotlink(w http.ResponseWriter, r *http.Request) {
hotlinkId := strings.Replace(r.URL.Path, "/hotlink/", "", 1)
file, ok := storage.GetFileByHotlink(hotlinkId)
if !ok {
- w.Header().Set("Content-Type", "image/png")
+ w.Header().Set("Content-Type", "image/svg+xml")
_, err := w.Write(imageExpiredPicture)
helper.Check(err)
return
@@ -487,7 +502,7 @@ func showE2ESetup(w http.ResponseWriter, r *http.Request) {
return
}
e2einfo := database.GetEnd2EndInfo()
- err := templateFolder.ExecuteTemplate(w, "e2esetup", e2ESetupView{HasBeenSetup: e2einfo.HasBeenSetUp()})
+ err := templateFolder.ExecuteTemplate(w, "e2esetup", e2ESetupView{HasBeenSetup: e2einfo.HasBeenSetUp(), PublicName: configuration.Get().PublicName})
helper.Check(err)
}
@@ -496,17 +511,22 @@ type DownloadView struct {
Name string
Size string
Id string
+ Cipher string
+ PublicName string
IsFailedLogin bool
IsAdminView bool
+ IsDownloadView bool
+ IsPasswordView bool
ClientSideDecryption bool
EndToEndEncryption bool
UsesHttps bool
- Cipher string
}
type e2ESetupView struct {
- IsAdminView bool
- HasBeenSetup bool
+ IsAdminView bool
+ IsDownloadView bool
+ HasBeenSetup bool
+ PublicName string
}
// UploadView contains parameters for the admin menu template
@@ -516,25 +536,27 @@ type UploadView struct {
Url string
HotlinkUrl string
GenericHotlinkUrl string
- TimeNow int64
+ DefaultPassword string
+ Logs string
+ PublicName string
IsAdminView bool
+ IsDownloadView bool
IsApiView bool
- MaxFileSize int
IsLogoutAvailable bool
- DefaultDownloads int
- DefaultExpiry int
- DefaultPassword string
DefaultUnlimitedDownload bool
DefaultUnlimitedTime bool
EndToEndEncryption bool
+ MaxFileSize int
+ DefaultDownloads int
+ DefaultExpiry int
ActiveView int
- Logs string
+ TimeNow int64
}
// ViewMain is the identifier for the main menu
const ViewMain = 0
-// ViewLogs is the identifier for the log viever menu
+// ViewLogs is the identifier for the log viewer menu
const ViewLogs = 1
// ViewAPI is the identifier for the API menu
@@ -583,15 +605,18 @@ func (u *UploadView) convertGlobalConfig(view int) *UploadView {
}
}
- u.Url = configuration.Get().ServerUrl + "d?id="
- u.HotlinkUrl = configuration.Get().ServerUrl + "hotlink/"
- u.GenericHotlinkUrl = configuration.Get().ServerUrl + "downloadFile?id="
+ config := configuration.Get()
+
+ u.Url = config.ServerUrl + "d?id="
+ u.HotlinkUrl = config.ServerUrl + "hotlink/"
+ u.GenericHotlinkUrl = config.ServerUrl + "downloadFile?id="
u.Items = result
+ u.PublicName = config.PublicName
u.ApiKeys = resultApi
u.TimeNow = time.Now().Unix()
u.IsAdminView = true
u.ActiveView = view
- u.MaxFileSize = configuration.Get().MaxFileSizeMB
+ u.MaxFileSize = config.MaxFileSizeMB
u.IsLogoutAvailable = authentication.IsLogoutAvailable()
defaultValues := database.GetUploadDefaults()
u.DefaultDownloads = defaultValues.Downloads
@@ -599,7 +624,7 @@ func (u *UploadView) convertGlobalConfig(view int) *UploadView {
u.DefaultPassword = defaultValues.Password
u.DefaultUnlimitedDownload = defaultValues.UnlimitedDownload
u.DefaultUnlimitedTime = defaultValues.UnlimitedTime
- u.EndToEndEncryption = configuration.Get().Encryption.Level == encryption.EndToEndEncryption
+ u.EndToEndEncryption = config.Encryption.Level == encryption.EndToEndEncryption
return u
}
@@ -698,7 +723,9 @@ func addNoCacheHeader(w http.ResponseWriter) {
// A view containing parameters for a generic template
type genericView struct {
- IsAdminView bool
- RedirectUrl string
- ErrorId int
+ IsAdminView bool
+ IsDownloadView bool
+ PublicName string
+ RedirectUrl string
+ ErrorId int
}
diff --git a/internal/webserver/Webserver_test.go b/internal/webserver/Webserver_test.go
index d85b6062..09d2c741 100644
--- a/internal/webserver/Webserver_test.go
+++ b/internal/webserver/Webserver_test.go
@@ -12,7 +12,6 @@ import (
"github.com/forceu/gokapi/internal/webserver/authentication"
"github.com/r3labs/sse/v2"
"html/template"
- "io/fs"
"os"
"strings"
"testing"
@@ -35,13 +34,9 @@ func TestEmbedFs(t *testing.T) {
if err != nil {
t.Error("Unable to read templates")
}
- if !strings.Contains(templates.DefinedTemplates(), "app_name") {
+ if !strings.Contains(templates.DefinedTemplates(), "header") {
t.Error("Unable to parse templates")
}
- _, err = fs.Stat(staticFolderEmbedded, "web/static/expired.png")
- if err != nil {
- t.Error("Static webdir incomplete")
- }
}
func TestIndexRedirect(t *testing.T) {
@@ -326,7 +321,7 @@ func TestDownloadHotlink(t *testing.T) {
// Download expired hotlink
test.HttpPageResult(t, test.HttpTestConfig{
Url: "http://127.0.0.1:53843/hotlink/PhSs6mFtf8O5YGlLMfNw9rYXx9XRNkzCnJZpQBi7inunv3Z4A.jpg",
- RequiredContent: []string{"Created with GIMP"},
+ RequiredContent: []string{"The requested image has expired"},
})
}
diff --git a/internal/webserver/web/static/expired.png b/internal/webserver/web/static/expired.png
deleted file mode 100644
index f2c515cb..00000000
Binary files a/internal/webserver/web/static/expired.png and /dev/null differ
diff --git a/internal/webserver/web/static/js/end2end_download.js b/internal/webserver/web/static/js/end2end_download.js
index 61dd686a..3f6d2707 100644
--- a/internal/webserver/web/static/js/end2end_download.js
+++ b/internal/webserver/web/static/js/end2end_download.js
@@ -1,6 +1,6 @@
function parseHashValue(id) {
- let key = localStorage.getItem("key-" + id);
- let filename = localStorage.getItem("fn-" + id);
+ let key = sessionStorage.getItem("key-" + id);
+ let filename = sessionStorage.getItem("fn-" + id);
if (key === null || filename === null) {
hash = window.location.hash.substr(1);
@@ -20,8 +20,8 @@ function parseHashValue(id) {
redirectToE2EError();
return;
}
- localStorage.setItem("key-" + id, info.c);
- localStorage.setItem("fn-" + id, info.f);
+ sessionStorage.setItem("key-" + id, info.c);
+ sessionStorage.setItem("fn-" + id, info.f);
}
}
diff --git a/internal/webserver/web/static/js/min/end2end_download.min.js b/internal/webserver/web/static/js/min/end2end_download.min.js
index 2173f110..7f40697b 100644
--- a/internal/webserver/web/static/js/min/end2end_download.min.js
+++ b/internal/webserver/web/static/js/min/end2end_download.min.js
@@ -1 +1 @@
-function parseHashValue(e){let t=localStorage.getItem("key-"+e),n=localStorage.getItem("fn-"+e);if(t===null||n===null){if(hash=window.location.hash.substr(1),hash.length<50){redirectToE2EError();return}let t;try{let e=atob(hash);t=JSON.parse(e)}catch{redirectToE2EError();return}if(!isCorrectJson(t)){redirectToE2EError();return}localStorage.setItem("key-"+e,t.c),localStorage.setItem("fn-"+e,t.f)}}function isCorrectJson(e){return e.f!==void 0&&e.c!==void 0&&typeof e.f=="string"&&typeof e.c=="string"&&e.f!=""&&e.c!=""}function redirectToE2EError(){window.location="./error?e2e"}
\ No newline at end of file
+function parseHashValue(e){let t=sessionStorage.getItem("key-"+e),n=sessionStorage.getItem("fn-"+e);if(t===null||n===null){if(hash=window.location.hash.substr(1),hash.length<50){redirectToE2EError();return}let t;try{let e=atob(hash);t=JSON.parse(e)}catch{redirectToE2EError();return}if(!isCorrectJson(t)){redirectToE2EError();return}sessionStorage.setItem("key-"+e,t.c),sessionStorage.setItem("fn-"+e,t.f)}}function isCorrectJson(e){return e.f!==void 0&&e.c!==void 0&&typeof e.f=="string"&&typeof e.c=="string"&&e.f!=""&&e.c!=""}function redirectToE2EError(){window.location="./error?e2e"}
\ No newline at end of file
diff --git a/internal/webserver/web/templates/expired_file_svg.tmpl b/internal/webserver/web/templates/expired_file_svg.tmpl
new file mode 100644
index 00000000..d216f46f
--- /dev/null
+++ b/internal/webserver/web/templates/expired_file_svg.tmpl
@@ -0,0 +1,5 @@
+
diff --git a/internal/webserver/web/templates/html_download.tmpl b/internal/webserver/web/templates/html_download.tmpl
index ef0cdc0f..1a847cab 100644
--- a/internal/webserver/web/templates/html_download.tmpl
+++ b/internal/webserver/web/templates/html_download.tmpl
@@ -78,10 +78,10 @@
async function DownloadEncrypted() {
try {
{{ if .EndToEndEncryption }}
- let key = localStorage.getItem("key-{{ .Id }}");
- localStorage.removeItem("key-{{ .Id }}");
- let filename = localStorage.getItem("fn-{{ .Id }}");
- localStorage.removeItem("fn-{{ .Id }}");
+ let key = sessionStorage.getItem("key-{{ .Id }}");
+ sessionStorage.removeItem("key-{{ .Id }}");
+ let filename = sessionStorage.getItem("fn-{{ .Id }}");
+ sessionStorage.removeItem("fn-{{ .Id }}");
{{ else }}
let key = "{{ .Cipher }}";
{{ end }}
@@ -141,7 +141,9 @@
{{ end }}
{{ if .EndToEndEncryption }}
{{ end }}
diff --git a/internal/webserver/web/templates/html_header.tmpl b/internal/webserver/web/templates/html_header.tmpl
index d1681fbd..53b72a32 100644
--- a/internal/webserver/web/templates/html_header.tmpl
+++ b/internal/webserver/web/templates/html_header.tmpl
@@ -13,7 +13,7 @@
{{ if .IsAdminView }}
-
{{template "app_name"}} Admin
+ {{.PublicName}} Admin
@@ -31,7 +31,15 @@
}
{{ else }}
- {{template "app_name"}}
+ {{ if .IsDownloadView }}
+ {{ if .IsPasswordView }}
+ {{.PublicName}}: Password required
+ {{ else }}
+ {{.PublicName}}: {{.Name}}
+ {{end }}
+ {{ else }}
+ {{.PublicName}}
+ {{end }}