mirror of
https://github.com/foomo/gofoomo.git
synced 2025-10-16 12:25:44 +00:00
124 lines
3.4 KiB
Go
124 lines
3.4 KiB
Go
package handler
|
|
|
|
import (
|
|
"mime"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/foomo/gofoomo/foomo"
|
|
"github.com/foomo/gofoomo/proxy/utils"
|
|
)
|
|
|
|
// StaticFiles handles serving static files from the local file system. It knows about
|
|
// foomos hierarchy and serves files from the htdocs directories of modules.
|
|
// Currently it will also serve files of disabled modules.
|
|
type StaticFiles struct {
|
|
foomo *foomo.Foomo
|
|
}
|
|
|
|
// NewStaticFiles constructor
|
|
func NewStaticFiles(foomo *foomo.Foomo) *StaticFiles {
|
|
sf := new(StaticFiles)
|
|
sf.foomo = foomo
|
|
return sf
|
|
}
|
|
|
|
// HandlesRequest request handler implementation
|
|
func (files *StaticFiles) HandlesRequest(incomingRequest *http.Request) bool {
|
|
if strings.HasPrefix(incomingRequest.URL.Path, "/foomo/modulesVar/") {
|
|
return true
|
|
}
|
|
if strings.HasPrefix(incomingRequest.URL.Path, "/foomo/modules/") {
|
|
parts := strings.Split(incomingRequest.URL.Path, "/")
|
|
if len(parts) > 3 {
|
|
moduleNameParts := strings.Split(parts[3], "-")
|
|
if strings.HasSuffix(parts[len(parts)-1], ".php") {
|
|
return false
|
|
}
|
|
return fileExists(files.foomo.GetModuleHtdocsDir(moduleNameParts[0]) + "/" + strings.Join(parts[4:], "/"))
|
|
}
|
|
return false
|
|
}
|
|
return false
|
|
}
|
|
|
|
func fileExists(filename string) bool {
|
|
_, err := os.Stat(filename)
|
|
return err == nil
|
|
}
|
|
|
|
func (files *StaticFiles) ServeHTTP(w http.ResponseWriter, incomingRequest *http.Request) {
|
|
parts := strings.Split(incomingRequest.URL.Path, "/")
|
|
path := strings.Join(parts[4:], "/")
|
|
moduleNameParts := strings.Split(parts[3], "-")
|
|
moduleName := moduleNameParts[0]
|
|
var moduleDir string
|
|
if strings.HasPrefix(incomingRequest.URL.Path, "/foomo/modules/") {
|
|
moduleDir = files.foomo.GetModuleHtdocsDir(moduleName)
|
|
} else {
|
|
moduleDir = files.foomo.GetModuleHtdocsVarDir(moduleName)
|
|
}
|
|
fullName := filepath.Join(moduleDir, path)
|
|
// validate path
|
|
absPath, errAbs := filepath.Abs(fullName)
|
|
if errAbs != nil {
|
|
http.Error(w, "forbidden", http.StatusForbidden)
|
|
return
|
|
}
|
|
if !strings.HasPrefix(absPath, moduleDir) {
|
|
http.Error(w, "forbidden", http.StatusForbidden)
|
|
return
|
|
}
|
|
// check file
|
|
fileInfo, errStat := os.Stat(fullName)
|
|
if errStat != nil {
|
|
switch true {
|
|
case os.IsNotExist(errStat):
|
|
http.Error(w, "not found", http.StatusNotFound)
|
|
case os.IsPermission(errStat):
|
|
http.Error(w, "forbidden", http.StatusForbidden)
|
|
default:
|
|
http.Error(w, "internal server error", http.StatusInternalServerError)
|
|
}
|
|
return
|
|
}
|
|
// open it
|
|
f, errOpen := os.Open(fullName)
|
|
defer f.Close()
|
|
if errOpen != nil {
|
|
http.Error(w, "internal server error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
// compression support
|
|
_, compress := getContentType(path)
|
|
w.Header().Set("Expires", time.Now().Add(time.Hour*24*365).Format(http.TimeFormat))
|
|
if compress && strings.Contains(incomingRequest.Header.Get("Accept-Encoding"), "gzip") {
|
|
w.Header().Set("Content-Encoding", "gzip")
|
|
crw := utils.NewCompressedResponseWriter(w)
|
|
defer crw.Close()
|
|
w = crw
|
|
}
|
|
// passing it on to std library
|
|
http.ServeContent(w, incomingRequest, f.Name(), fileInfo.ModTime(), f)
|
|
}
|
|
|
|
func getContentType(path string) (mimeType string, compress bool) {
|
|
parts := strings.Split(path, ".")
|
|
suffix := parts[len(parts)-1]
|
|
|
|
compress = false
|
|
|
|
switch suffix {
|
|
case "css", "js", "html", "htm", "ttf", "eot", "svg", "txt", "csv":
|
|
compress = true
|
|
}
|
|
mimeType = mime.TypeByExtension("." + suffix)
|
|
if mimeType == "" {
|
|
mimeType = "application/octet-stream"
|
|
}
|
|
return
|
|
}
|