diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 2cd7110..d08176b 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -19,4 +19,4 @@ jobs: submodules: true - name: Run - run: make ci + run: make test diff --git a/.gitignore b/.gitignore index ec40082..80d910e 100755 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ gossa-linux-arm gossa-linux-arm64 gossa-mac gossa-windows.exe +gossa_test.go .vscode test-fixture/* diff --git a/Makefile b/Makefile index 666b42b..063183e 100755 --- a/Makefile +++ b/Makefile @@ -3,42 +3,35 @@ build: make -C gossa-ui/ go vet && go fmt CGO_ENABLED=0 go build gossa.go - sleep 1 && rm gossa.go + rm gossa.go run: - make build ./gossa -verb=true test-fixture +run-ro: + ./gossa -verb=true -ro=true test-fixture + run-extra: - make build ./gossa -verb=true -prefix="/fancy-path/" -k=false -symlinks=true test-fixture test: - timeout 60 make run & - sleep 15 && cp src/gossa_test.go . && go test -run TestNormal - rm gossa_test.go - -killall gossa - -test-extra: - timeout 60 make run-extra & - sleep 15 && cp src/gossa_test.go . && go test -run TestExtra - rm gossa_test.go - -killall gossa - -ci: + make build + -rm gossa_test.go -@cd test-fixture && ln -s ../support . - make test - make test-extra - + cp src/gossa_test.go . + go test watch: - ls src/* gossa-ui/* | entr -rc make run + ls src/* gossa-ui/* | entr -rc make build run watch-extra: - ls src/* gossa-ui/* | entr -rc make run-extra + ls src/* gossa-ui/* | entr -rc make build run-extra + +watch-ro: + ls src/* gossa-ui/* | entr -rc make build run-ro -watch-ci: - ls src/* gossa-ui/* | entr -rc make ci +watch-test: + ls src/* gossa-ui/* | entr -rc make test build-all: cp src/gossa.go gossa.go diff --git a/gossa-ui b/gossa-ui index 6a14c31..ee49309 160000 --- a/gossa-ui +++ b/gossa-ui @@ -1 +1 @@ -Subproject commit 6a14c317ef43828b171f8478feff13bae224b3ac +Subproject commit ee49309b2ed0c2d48c627c1fddf3a36d50a8314b diff --git a/readme.md b/readme.md index 28c00aa..2e20527 100644 --- a/readme.md +++ b/readme.md @@ -14,13 +14,14 @@ a [simple UI](https://github.com/pldubouilh/gossa-ui) comes as default, featurin * πŸ” files/directories browser * πŸ“© drag-and-drop file/directory uploader + * πŸš€ lightweight, default ui weights 110kB and prints in ms * πŸ—ΊοΈ files handling - move/rename/delete * πŸ“Έ picture browser * πŸ“½οΈ video streaming * ✍️ simple text editor * ⌨️ keyboard shortcuts - * πŸ₯‚ speed - will easily fill available bandwidth - * πŸ”’ safe - easy/secure multi account setup + * πŸ₯‚ fast golang static server, easily fills available bandwidth + * πŸ”’ easy/secure multi account setup ### build built blobs are available on the [release page](https://github.com/pldubouilh/gossa/releases) - or simply `make build` this repo. diff --git a/src/gossa.go b/src/gossa.go index 48091d2..0d8e379 100755 --- a/src/gossa.go +++ b/src/gossa.go @@ -14,6 +14,7 @@ import ( "net/url" "os" "path/filepath" + "sort" "strconv" "strings" ) @@ -24,6 +25,7 @@ var extraPath = flag.String("prefix", "/", "url prefix at which gossa can be rea var symlinks = flag.Bool("symlinks", false, "follow symlinks \033[4mWARNING\033[0m: symlinks will by nature allow to escape the defined path (default: false)") var verb = flag.Bool("verb", false, "verbosity") var skipHidden = flag.Bool("k", true, "\nskip hidden files") +var ro = flag.Bool("ro", false, "read only mode (no upload, rename, move, etc...)") var initPath = "." var fs http.Handler @@ -39,6 +41,7 @@ type rowTemplate struct { type pageTemplate struct { Title template.HTML ExtraPath template.HTML + Ro bool RowsFiles []rowTemplate RowsFolders []rowTemplate } @@ -79,6 +82,7 @@ func humanize(bytes int64) string { func replyList(w http.ResponseWriter, fullPath string, path string) { _files, err := ioutil.ReadDir(fullPath) check(err) + sort.Slice(_files, func(i, j int) bool { return strings.ToLower(_files[i].Name()) < strings.ToLower(_files[j].Name()) }) if !strings.HasSuffix(path, "/") { path += "/" @@ -90,6 +94,7 @@ func replyList(w http.ResponseWriter, fullPath string, path string) { p.RowsFolders = append(p.RowsFolders, rowTemplate{"../", "../", "", "folder"}) } p.ExtraPath = template.HTML(html.EscapeString(*extraPath)) + p.Ro = *ro p.Title = template.HTML(html.EscapeString(title)) for _, el := range _files { @@ -188,8 +193,10 @@ func main() { initPath, err = filepath.Abs(initPath) check(err) - http.HandleFunc(*extraPath+"rpc", rpc) - http.HandleFunc(*extraPath+"post", upload) + if !*ro { + http.HandleFunc(*extraPath+"rpc", rpc) + http.HandleFunc(*extraPath+"post", upload) + } http.HandleFunc("/", doContent) fs = http.StripPrefix(*extraPath, http.FileServer(http.Dir(initPath))) fmt.Printf("Gossa startig on directory %s\nListening on http://%s:%s%s\n", initPath, *host, *port, *extraPath) diff --git a/src/gossa_test.go b/src/gossa_test.go index 51ad822..d3e8e0b 100644 --- a/src/gossa_test.go +++ b/src/gossa_test.go @@ -5,9 +5,12 @@ import ( "fmt" "io/ioutil" "net/http" + "os" + "os/exec" "regexp" "strings" "testing" + "time" ) func dieMaybe(t *testing.T, err error) { @@ -83,7 +86,7 @@ func fetchAndTestDefault(t *testing.T, url string) string { return body0 } -func doTest(t *testing.T, url string, testExtra bool) { +func doTestRegular(t *testing.T, url string, testExtra bool) { var payload, path, body0, body1, body2 string // ~~~~~~~~~~~~~~~~~ @@ -228,16 +231,99 @@ func doTest(t *testing.T, url string, testExtra bool) { if body0 != `ok` { t.Fatal("cleanup errored #2") } -} -func TestNormal(t *testing.T) { - fmt.Println("========== testing normal path ============") - doTest(t, "http://127.0.0.1:8001/", false) fmt.Printf("\r\n=========\r\n") } -func TestExtra(t *testing.T) { - fmt.Println("========== testing at fancy path ============") - doTest(t, "http://127.0.0.1:8001/fancy-path/", true) +func doTestReadonly(t *testing.T, url string) { + var payload, path, body0, body1 string + + // ~~~~~~~~~~~~~~~~~ + fmt.Println("\r\n~~~~~~~~~~ test fetching default path") + fetchAndTestDefault(t, url) + + // ~~~~~~~~~~~~~~~~~ + fmt.Println("\r\n~~~~~~~~~~ test fetching an invalid path - redirected to root") + fetchAndTestDefault(t, url+"../../") + fetchAndTestDefault(t, url+"hols/../../") + + // ~~~~~~~~~~~~~~~~~ + fmt.Println("\r\n~~~~~~~~~~ test fetching regular files") + body0 = get(t, url+"subdir_with%20space/file_with%20space.html") + body1 = get(t, url+"fancy-path/a") + if body0 != `spacious!! ` || body1 != `fancy! ` { + t.Fatal("fetching a regular file errored") + } + + // ~~~~~~~~~~~~~~~~~ + fmt.Println("\r\n~~~~~~~~~~ test fetching a invalid file") + path = "../../../../../../../../../../etc/passwd" + if get(t, url+path) != `error` { + t.Fatal("fetching a invalid file didnt errored") + } + + // ~~~~~~~~~~~~~~~~~ + fmt.Println("\r\n~~~~~~~~~~ test mkdir rpc") + body0 = postJSON(t, url+"rpc", `{"call":"mkdirp","args":["/AAA"]}`) + if body0 == `ok` { + t.Fatal("mkdir rpc passed - should not be allowed") + } + + // ~~~~~~~~~~~~~~~~~ + fmt.Println("\r\n~~~~~~~~~~ test post file") + path = "%2F%E1%84%92%E1%85%A1%20%E1%84%92%E1%85%A1" // "α„’α…‘ α„’α…‘" encoded + payload = "123 α„’α…‘" + body0 = postDummyFile(t, url, path, payload) + body1 = get(t, url+path) + if body0 == `ok` { + t.Fatal("post file passed - should not be allowed") + } + + // ~~~~~~~~~~~~~~~~~ + fmt.Println("\r\n~~~~~~~~~~ test mv rpc") + body0 = postJSON(t, url+"rpc", `{"call":"mv","args":["/AAA", "/hols/AAA"]}`) + body1 = fetchAndTestDefault(t, url) + if body0 == `ok` { + t.Fatal("mv rpc passed - should not be allowed") + } + + // ~~~~~~~~~~~~~~~~~ + fmt.Println("\r\n~~~~~~~~~~ test rm rpc & cleanup") + body0 = postJSON(t, url+"rpc", `{"call":"rm","args":["/hols/AAA"]}`) + if body0 == `ok` { + t.Fatal("cleanup passed - should not be allowed") + } + fmt.Printf("\r\n=========\r\n") } + +func startGossa(what string) int { + cmd := exec.Command("/usr/bin/make", what) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stdout + cmd.Start() + time.Sleep(3 * time.Second) + return cmd.Process.Pid +} + +func killallgossa() { + cmd := exec.Command("/usr/bin/killall", "gossa") + cmd.Start() +} + +func TestGossa(t *testing.T) { + startGossa("run") + fmt.Println("========== testing normal path ============") + doTestRegular(t, "http://127.0.0.1:8001/", false) + killallgossa() + + startGossa("run-extra") + fmt.Println("========== testing extras options ============") + doTestRegular(t, "http://127.0.0.1:8001/fancy-path/", true) + killallgossa() + + startGossa("run-ro") + fmt.Println("========== testing read only ============") + doTestReadonly(t, "http://127.0.0.1:8001/") + killallgossa() +}