Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package goshs for openSUSE:Factory checked in at 2026-03-11 20:57:19 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/goshs (Old) and /work/SRC/openSUSE:Factory/.goshs.new.8177 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "goshs" Wed Mar 11 20:57:19 2026 rev:2 rq:1338295 version:1.1.3 Changes: -------- --- /work/SRC/openSUSE:Factory/goshs/goshs.changes 2025-09-22 16:42:04.547451699 +0200 +++ /work/SRC/openSUSE:Factory/.goshs.new.8177/goshs.changes 2026-03-11 20:59:26.996140154 +0100 @@ -1,0 +2,15 @@ +Tue Dec 16 16:13:56 UTC 2025 - Martin Hauke <[email protected]> + +- Update to version 1.1.3 + * New Feature: Invisible mode. Read more on that at + https://goshs.de/en/usage/restrictions/index.html#be-invisible-invisible-mode + * made mDNS opt-in instead of opt-out. + +------------------------------------------------------------------- +Wed Nov 12 18:53:33 UTC 2025 - Martin Hauke <[email protected]> + +- Update to version 1.1.2 + * Fix upload behaviour. Details see issue #128 + * Remove Upload form from WebUI when read-only to address issue. + +------------------------------------------------------------------- Old: ---- goshs-1.1.1.tar.gz New: ---- goshs-1.1.3.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ goshs.spec ++++++ --- /var/tmp/diff_new_pack.CNaNqn/_old 2026-03-11 20:59:27.636166513 +0100 +++ /var/tmp/diff_new_pack.CNaNqn/_new 2026-03-11 20:59:27.640166677 +0100 @@ -16,14 +16,14 @@ # Name: goshs -Version: 1.1.1 +Version: 1.1.3 Release: 0 Summary: A simple HTTP server License: MIT Group: Productivity/Networking/Web/Servers URL: https://goshs.de/ #Git-Clone: https://github.com/patrickhener/goshs.git -Source: https://github.com/patrickhener/goshs/archive/refs/tags/v%{version}.tar.gz#/%{name}-%{version}.tar.gz +Source: https://github.com/patrickhener/goshs/archive/refs/tags/%{version}.tar.gz#/%{name}-%{version}.tar.gz Source1: vendor.tar.gz BuildRequires: go >= 1.24.1 BuildRequires: golang-packaging ++++++ goshs-1.1.1.tar.gz -> goshs-1.1.3.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/goshs-1.1.1/README.md new/goshs-1.1.3/README.md --- old/goshs-1.1.1/README.md 2025-09-22 09:54:08.000000000 +0200 +++ new/goshs-1.1.3/README.md 2025-12-16 10:29:53.000000000 +0100 @@ -1,4 +1,4 @@ - + [](https://github.com/patrickhener/goshs/blob/master/LICENSE)  [](https://github.com/patrickhener/goshs/issues) @@ -47,6 +47,7 @@ * Key Auth * Password Auth * Silent mode (no webserver output) +* Invisible mode (no output whatsoever) * Retrieve json on cli * Drop user privileges before execution (Unix only) * Example: Run on port 80, but process is "www-data" @@ -149,6 +150,10 @@ [](https://discord.gg/3ZnskY8HcJ) +# Star History + +[](https://www.star-history.com/#patrickhener/goshs&type=date&legend=top-left) + # Credits A special thank you goes to *sc0tfree* for inspiring this project with his project [updog](https://github.com/sc0tfree/updog) written in Python. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/goshs-1.1.1/config/config.go new/goshs-1.1.3/config/config.go --- old/goshs-1.1.1/config/config.go 2025-09-22 09:54:08.000000000 +0200 +++ new/goshs-1.1.3/config/config.go 2025-12-16 10:29:53.000000000 +0100 @@ -37,6 +37,7 @@ NoDelete bool `json:"no_delete"` Verbose bool `json:"verbose"` Silent bool `json:"silent"` + Invisible bool `json:"invisible"` RunningUser string `json:"running_user"` CLI bool `json:"cli"` Embedded bool `json:"embedded"` @@ -96,6 +97,7 @@ NoDelete: false, Verbose: false, Silent: false, + Invisible: false, RunningUser: "", CLI: false, Embedded: false, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/goshs-1.1.1/example/goshs.json.example new/goshs-1.1.3/example/goshs.json.example --- old/goshs-1.1.1/example/goshs.json.example 2025-09-22 09:54:08.000000000 +0200 +++ new/goshs-1.1.3/example/goshs.json.example 2025-12-16 10:29:53.000000000 +0100 @@ -25,6 +25,7 @@ "no_delete": false, "verbose": false, "silent": false, + "invisible": false, "running_user": "", "cli": false, "embedded": false, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/goshs-1.1.1/go.mod new/goshs-1.1.3/go.mod --- old/goshs-1.1.1/go.mod 2025-09-22 09:54:08.000000000 +0200 +++ new/goshs-1.1.3/go.mod 2025-12-16 10:29:53.000000000 +0100 @@ -18,8 +18,8 @@ github.com/stretchr/testify v1.10.0 github.com/studio-b12/gowebdav v0.10.0 github.com/testcontainers/testcontainers-go v0.37.0 - golang.org/x/crypto v0.40.0 - golang.org/x/net v0.42.0 + golang.org/x/crypto v0.45.0 + golang.org/x/net v0.47.0 software.sslmate.com/src/go-pkcs12 v0.5.0 ) @@ -73,11 +73,11 @@ go.opentelemetry.io/otel/metric v1.36.0 // indirect go.opentelemetry.io/otel/trace v1.36.0 // indirect go.opentelemetry.io/proto/otlp v1.6.0 // indirect - golang.org/x/mod v0.25.0 // indirect - golang.org/x/sync v0.16.0 // indirect - golang.org/x/sys v0.34.0 // indirect - golang.org/x/term v0.33.0 // indirect - golang.org/x/text v0.27.0 // indirect - golang.org/x/tools v0.34.0 // indirect + golang.org/x/mod v0.29.0 // indirect + golang.org/x/sync v0.18.0 // indirect + golang.org/x/sys v0.38.0 // indirect + golang.org/x/term v0.37.0 // indirect + golang.org/x/text v0.31.0 // indirect + golang.org/x/tools v0.38.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/goshs-1.1.1/go.sum new/goshs-1.1.3/go.sum --- old/goshs-1.1.1/go.sum 2025-09-22 09:54:08.000000000 +0200 +++ new/goshs-1.1.3/go.sum 2025-12-16 10:29:53.000000000 +0100 @@ -174,8 +174,8 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= -golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= +golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= +golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -184,8 +184,8 @@ golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= -golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= +golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -199,8 +199,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= -golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -210,8 +210,8 @@ golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -231,8 +231,8 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= -golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -242,8 +242,8 @@ golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= -golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg= -golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= +golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= @@ -253,8 +253,8 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= -golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -266,8 +266,8 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= -golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= +golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= +golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/goshs-1.1.1/goshsversion/version.go new/goshs-1.1.3/goshsversion/version.go --- old/goshs-1.1.1/goshsversion/version.go 2025-09-22 09:54:08.000000000 +0200 +++ new/goshs-1.1.3/goshsversion/version.go 2025-12-16 10:29:53.000000000 +0100 @@ -1,3 +1,3 @@ package goshsversion -var GoshsVersion = "v1.1.1" +var GoshsVersion = "1.1.3" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/goshs-1.1.1/httpserver/error.go new/goshs-1.1.3/httpserver/error.go --- old/goshs-1.1.1/httpserver/error.go 2025-09-22 09:54:08.000000000 +0200 +++ new/goshs-1.1.3/httpserver/error.go 2025-12-16 10:29:53.000000000 +0100 @@ -9,7 +9,25 @@ "github.com/patrickhener/goshs/utils" ) +func (fs *FileServer) handleInvisible(w http.ResponseWriter) { + hj, ok := w.(http.Hijacker) + if !ok { + return + } + + conn, _, err := hj.Hijack() + if err != nil { + return + } + + conn.Close() +} + func (fs *FileServer) handleError(w http.ResponseWriter, req *http.Request, err error, status int) { + if fs.Invisible { + fs.handleInvisible(w) + return + } // Set header to status w.WriteHeader(status) @@ -22,8 +40,14 @@ // Construct error for template filling e.ErrorCode = status e.ErrorMessage = err.Error() - e.Directory = &directory{ - AbsPath: path.Join(fs.Webroot, req.URL.Path), + if fs.Silent { + e.Directory = &directory{ + AbsPath: "silent mode", + } + } else { + e.Directory = &directory{ + AbsPath: path.Join(fs.Webroot, req.URL.Path), + } } e.GoshsVersion = fs.Version diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/goshs-1.1.1/httpserver/handler.go new/goshs-1.1.3/httpserver/handler.go --- old/goshs-1.1.1/httpserver/handler.go 2025-09-22 09:54:08.000000000 +0200 +++ new/goshs-1.1.3/httpserver/handler.go 2025-12-16 10:29:53.000000000 +0100 @@ -106,22 +106,34 @@ return true } if _, ok := req.URL.Query()["cbDown"]; ok { - if !fs.NoClipboard { + if !fs.NoClipboard && !fs.Invisible { fs.cbDown(w, req) return true } } if _, ok := req.URL.Query()["bulk"]; ok { - fs.bulkDownload(w, req) + if !fs.Invisible { + fs.bulkDownload(w, req) + } else { + fs.handleInvisible(w) + } return true } if _, ok := req.URL.Query()["static"]; ok { - fs.static(w, req) + if !fs.Invisible { + fs.static(w, req) + } else { + fs.handleInvisible(w) + } return true } if _, ok := req.URL.Query()["embedded"]; ok { if err := fs.embedded(w, req); err != nil { - logger.LogRequest(req, http.StatusNotFound, fs.Verbose, fs.Webhook) + if !fs.Invisible { + logger.LogRequest(req, http.StatusNotFound, fs.Verbose, fs.Webhook) + } else { + fs.handleInvisible(w) + } return true } logger.LogRequest(req, http.StatusOK, fs.Verbose, fs.Webhook) @@ -137,16 +149,24 @@ } } if _, ok := req.URL.Query()["share"]; ok { - fs.CreateShareHandler(w, req) + if !fs.Invisible { + fs.CreateShareHandler(w, req) + } else { + fs.handleInvisible(w) + } return true } if _, ok := req.URL.Query()["token"]; ok { - switch req.Method { - case http.MethodGet: - fs.ShareHandler(w, req) - case http.MethodDelete: - fs.DeleteShareHandler(w, req) - default: + if !fs.Invisible { + switch req.Method { + case http.MethodGet: + fs.ShareHandler(w, req) + case http.MethodDelete: + fs.DeleteShareHandler(w, req) + default: + } + } else { + fs.handleInvisible(w) } return true } @@ -213,14 +233,16 @@ } // Applies custom auth for file based acls -func (fileS *FileServer) applyCustomAuth(w http.ResponseWriter, req *http.Request, acl configFile) { +func (fileS *FileServer) applyCustomAuth(w http.ResponseWriter, req *http.Request, acl configFile) bool { if acl.Auth != "" { - w.Header().Set("WWW-Authenticate", `Basic realm="Filebased Restricted"`) + if !fileS.Invisible { + w.Header().Set("WWW-Authenticate", `Basic realm="Filebased Restricted"`) + } username, password, authOK := req.BasicAuth() if !authOK { fileS.handleError(w, req, fmt.Errorf("%s", "not authorized"), http.StatusUnauthorized) - return + return false } user := strings.Split(acl.Auth, ":")[0] @@ -228,9 +250,10 @@ if username != user || !checkPasswordHash(password, passwordHash) { fileS.handleError(w, req, fmt.Errorf("%s", "not authorized"), http.StatusUnauthorized) - return + return false } } + return true } func (fileS *FileServer) constructEmbedded() []item { @@ -358,6 +381,7 @@ NoClipboard: fileS.NoClipboard, NoDelete: fileS.NoDelete, SharedLinks: fileS.SharedLinks, + ReadOnly: fileS.ReadOnly, } files := []string{"static/templates/index.html", "static/templates/header.tmpl", "static/templates/footer.tmpl", "static/templates/scripts_index.tmpl"} @@ -458,6 +482,11 @@ } func (fileS *FileServer) processDir(w http.ResponseWriter, req *http.Request, file *os.File, relpath string, jsonOutput bool, acl configFile) { + // Early break for invisible mode + if fileS.Invisible { + fileS.handleInvisible(w) + return + } // Read directory FileInfo fis, err := file.Readdir(-1) if err != nil { @@ -469,7 +498,10 @@ relpath = strings.TrimLeft(relpath, "\\") // Apply Custom Auth if there is any due to file based acl - fileS.applyCustomAuth(w, req, acl) + if ok := fileS.applyCustomAuth(w, req, acl); !ok { + fileS.handleError(w, req, err, http.StatusUnauthorized) + return + } // Construct items list items := fileS.constructItems(fis, relpath, acl, req) @@ -479,6 +511,10 @@ // if ?json output json listing if jsonOutput { + if fileS.Silent { + fileS.handleError(w, req, fmt.Errorf("%s", "json output deactivated in silent mode"), http.StatusNotFound) + return + } returnJsonDirListing(w, items) return } @@ -498,7 +534,9 @@ // Apply Custom Auth if there if acl.Auth != "" { - w.Header().Set("WWW-Authenticate", `Basic realm="Filebased Restricted"`) + if !fs.Invisible { + w.Header().Set("WWW-Authenticate", `Basic realm="Filebased Restricted"`) + } username, password, authOK := req.BasicAuth() if !authOK { @@ -569,7 +607,11 @@ // socket will handle the socket connection func (fs *FileServer) socket(w http.ResponseWriter, req *http.Request) { - ws.ServeWS(fs.Hub, w, req) + ok := ws.ServeWS(fs.Hub, w, req) + if !ok { + fs.handleError(w, req, fmt.Errorf("failed to serve websocket"), http.StatusInternalServerError) + return + } } // deleteFile will delete a file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/goshs-1.1.1/httpserver/log.go new/goshs-1.1.3/httpserver/log.go --- old/goshs-1.1.1/httpserver/log.go 2025-09-22 09:54:08.000000000 +0200 +++ new/goshs-1.1.3/httpserver/log.go 2025-12-16 10:29:53.000000000 +0100 @@ -8,6 +8,11 @@ func (fs *FileServer) logOnly(w http.ResponseWriter, req *http.Request) { logger.LogRequest(req, http.StatusOK, fs.Verbose, fs.Webhook) - w.WriteHeader(200) - w.Write([]byte("ok\n")) + if fs.Invisible { + // In invisible mode, do not respond + fs.handleInvisible(w) + } else { + w.WriteHeader(200) + w.Write([]byte("ok\n")) + } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/goshs-1.1.1/httpserver/middleware.go new/goshs-1.1.3/httpserver/middleware.go --- old/goshs-1.1.1/httpserver/middleware.go 2025-09-22 09:54:08.000000000 +0200 +++ new/goshs-1.1.3/httpserver/middleware.go 2025-12-16 10:29:53.000000000 +0100 @@ -87,9 +87,72 @@ }) } +// InvisibleBasicAuthMiddleware is a middleware to handle basic auth in invisible mode +func (fs *FileServer) InvisibleBasicAuthMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + auth := r.Header.Get("Authorization") + if !strings.HasPrefix(auth, "Basic ") { + fs.handleInvisible(w) + return + } + + authVal := auth[len("Basic "):] + // Cache check + authCacheMutex.RLock() + cachedOK := authCache[authVal] + authCacheMutex.RUnlock() + + if cachedOK { + next.ServeHTTP(w, r) + return + } + + //Check if provided password is a bcrypt hash + if strings.HasPrefix(fs.Pass, "$2a$") { + username, password, authOK := r.BasicAuth() + if !authOK { + fs.handleInvisible(w) + return + } + + if username != fs.User { + fs.handleInvisible(w) + return + } + + if err := bcrypt.CompareHashAndPassword([]byte(fs.Pass), []byte(password)); err != nil { + fs.handleInvisible(w) + return + } + } else { + username, password, authOK := r.BasicAuth() + if !authOK { + fs.handleInvisible(w) + return + } + + if username != fs.User || password != fs.Pass { + fs.handleInvisible(w) + return + } + } + + authCacheMutex.Lock() + authCache[authVal] = true + authCacheMutex.Unlock() + + next.ServeHTTP(w, r) + }) +} + // ServerHeaderMiddleware sets a custom Server header for all responses func (fs *FileServer) ServerHeaderMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Invisible mode + if fs.Invisible { + next.ServeHTTP(w, r) + return + } serverHeader := fmt.Sprintf("goshs/%s (%s; %s)", fs.Version, runtime.GOOS, runtime.Version()) w.Header().Set("Server", serverHeader) next.ServeHTTP(w, r) @@ -104,8 +167,13 @@ if !fs.Whitelist.IsAllowed(clientIP) { logger.Warnf("[WHITELIST] Access denied for IP: %s", clientIP) - http.Error(w, "Access Denied", http.StatusForbidden) - return + if !fs.Invisible { + http.Error(w, "Access Denied", http.StatusForbidden) + return + } else { + fs.handleInvisible(w) + return + } } // logger.Infof("[WHITELIST] Access granted for IP: %s", clientIP) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/goshs-1.1.1/httpserver/server.go new/goshs-1.1.3/httpserver/server.go --- old/goshs-1.1.1/httpserver/server.go 2025-09-22 09:54:08.000000000 +0200 +++ new/goshs-1.1.3/httpserver/server.go 2025-12-16 10:29:53.000000000 +0100 @@ -30,8 +30,13 @@ logger.Warnf("You are using basic auth without SSL. Your credentials will be transferred in cleartext. Consider using -s, too.") } logger.Infof("Using basic auth with user '%s' and password '%s'", fs.User, fs.Pass) - // Use middleware - mux.Use(fs.BasicAuthMiddleware) + if fs.Invisible { + // Use invisible basic auth middleware + mux.Use(fs.InvisibleBasicAuthMiddleware) + } else { + // Use middleware + mux.Use(fs.BasicAuthMiddleware) + } } // IP Whitelist Middleware diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/goshs-1.1.1/httpserver/static/templates/index.html new/goshs-1.1.3/httpserver/static/templates/index.html --- old/goshs-1.1.1/httpserver/static/templates/index.html 2025-09-22 09:54:08.000000000 +0200 +++ new/goshs-1.1.3/httpserver/static/templates/index.html 2025-12-16 10:29:53.000000000 +0100 @@ -4,6 +4,7 @@ <div class="row h-100"> <!-- 6: File Listing --> <div class="col-xl-6"> + {{ if not .ReadOnly}} <!-- Heading Row --> <div class="row"> <div class="col mb-2"> @@ -30,6 +31,7 @@ </form> </div> </div> + {{ end }} <!-- Checkbox Control Row --> <div class="row"> <div class="col mb-2"> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/goshs-1.1.1/httpserver/structs.go new/goshs-1.1.3/httpserver/structs.go --- old/goshs-1.1.1/httpserver/structs.go 2025-09-22 09:54:08.000000000 +0200 +++ new/goshs-1.1.3/httpserver/structs.go 2025-12-16 10:29:53.000000000 +0100 @@ -20,6 +20,7 @@ NoClipboard bool NoDelete bool SharedLinks map[string]SharedLink + ReadOnly bool } type directory struct { @@ -75,6 +76,7 @@ NoClipboard bool NoDelete bool Silent bool + Invisible bool Embedded bool Verbose bool Webhook webhook.Webhook diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/goshs-1.1.1/main.go new/goshs-1.1.3/main.go --- old/goshs-1.1.1/main.go 2025-09-22 09:54:08.000000000 +0200 +++ new/goshs-1.1.3/main.go 2025-12-16 10:29:53.000000000 +0100 @@ -27,7 +27,7 @@ ip = "0.0.0.0" cli = false webroot = "." - uploadFolder = "." + uploadFolder = "" ssl = false selfsigned = false letsencrypt = false @@ -64,7 +64,8 @@ webhookEventsParsed = []string{} whitelist = "" trustedProxies = "" - noMDNS = false + MDNS = false + invisible = false ) // Man page @@ -86,6 +87,7 @@ -nc, --no-clipboard Disable the clipboard sharing (default: false) -nd, --no-delete Disable the delete option (default: false) -si, --silent Running without dir listing (default: false) + -I, --invisible Invisible mode (default: false) -c, --cli Enable cli (only with auth and tls) (default: false) -e, --embedded Show embedded files in UI (default: false) -o, --output Write output to logfile (default: false) @@ -115,8 +117,8 @@ -H, --hash Hash a password for file based ACLs Connection restriction: - -ipw, --ip-whitelist Comma seperated list of IPs to whitelist - -tpw, --trusted-proxy-whitelist Comma seperated list of trusted proxies + -ipw, --ip-whitelist Comma separated list of IPs to whitelist + -tpw, --trusted-proxy-whitelist Comma separated list of trusted proxies Webhook options: -W, --webhook Enable webhook support (default: false) @@ -131,7 +133,7 @@ -P --print-config Print sample config to STDOUT (default: false) -u --user Drop privs to user (unix only) (default: current user) --update Update goshs to most recent version - -nm --no-mdns Disable zeroconf mDNS registration (default: false) + -m --mdns Disable zeroconf mDNS registration (default: false) -V --verbose Activate verbose log output (default: false) -v Print the current goshs version @@ -234,8 +236,10 @@ flag.StringVar(&trustedProxies, "trusted-proxy-whitelist", trustedProxies, "") flag.StringVar(&uploadFolder, "uf", uploadFolder, "") flag.StringVar(&uploadFolder, "upload-folder", uploadFolder, "") - flag.BoolVar(&noMDNS, "nm", noMDNS, "Disable zeroconf mDNS registration") - flag.BoolVar(&noMDNS, "no-mdns", noMDNS, "Disable zeroconf mDNS registration") + flag.BoolVar(&MDNS, "m", MDNS, "Enable zeroconf mDNS registration") + flag.BoolVar(&MDNS, "mdns", MDNS, "Enable zeroconf mDNS registration") + flag.BoolVar(&invisible, "I", invisible, "Enable invisible mode") + flag.BoolVar(&invisible, "invisible", invisible, "Enable invisible mode") updateGoshs := flag.Bool("update", false, "update") hash := flag.Bool("H", false, "hash") @@ -271,6 +275,17 @@ } func sanityChecks() { + // Sanity check for invisible mode + if invisible { + if sftp || webdav { + sftp = false + webdav = false + MDNS = false + silent = false + logger.Warn("Invisible mode activated, disabling SFTP, WebDAV, silent mode and mDNS support") + } + } + // Sanity check for upload only vs read only if uploadOnly && readOnly { logger.Fatal("You can only select either 'upload only' or 'read only', not both.") @@ -339,6 +354,11 @@ os.Exit(0) } + // Set uploadFolder to webroot if not set + if uploadFolder == "" { + uploadFolder = webroot + } + if configFile != "" { cfg, err := config.Load(configFile) if err != nil { @@ -391,6 +411,7 @@ sftpHostKeyfile = cfg.SFTPHostKeyFile whitelist = cfg.Whitelist trustedProxies = cfg.TrustedProxies + invisible = cfg.Invisible // Abspath for webroot // Trim trailing / for linux/mac and \ for windows @@ -539,6 +560,7 @@ NoClipboard: noClipboard, NoDelete: noDelete, Silent: silent, + Invisible: invisible, Embedded: embedded, Webhook: *webhook, Verbose: verbose, @@ -547,7 +569,7 @@ } // Zeroconf mDNS - if !noMDNS { + if MDNS { err = utils.RegisterZeroconfMDNS(ssl, port, webdav, webdavPort, sftp, sftpPort) if err != nil { logger.Warnf("error registering zeroconf mDNS: %+v", err) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/goshs-1.1.1/ws/client.go new/goshs-1.1.3/ws/client.go --- old/goshs-1.1.1/ws/client.go 2025-09-22 09:54:08.000000000 +0200 +++ new/goshs-1.1.3/ws/client.go 2025-12-16 10:29:53.000000000 +0100 @@ -150,11 +150,11 @@ } // ServeWS will handle the socket connections -func ServeWS(hub *Hub, w http.ResponseWriter, r *http.Request) { +func ServeWS(hub *Hub, w http.ResponseWriter, r *http.Request) bool { conn, err := websocket.Accept(w, r, nil) if err != nil { logger.Errorf("Failed to upgrade ws: %+v", err) - return + return false } client := &Client{hub: hub, conn: conn, send: make(chan []byte, 1024)} @@ -162,6 +162,8 @@ go client.writePump() go client.readPump() + + return true } func (c *Client) refreshClipboard() { ++++++ vendor.tar.gz ++++++ ++++ 12147 lines of diff (skipped)
