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-05-28 17:28:35
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/goshs (Old)
 and      /work/SRC/openSUSE:Factory/.goshs.new.1937 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "goshs"

Thu May 28 17:28:35 2026 rev:9 rq:1355542 version:2.0.9

Changes:
--------
--- /work/SRC/openSUSE:Factory/goshs/goshs.changes      2026-05-13 
20:59:32.329637344 +0200
+++ /work/SRC/openSUSE:Factory/.goshs.new.1937/goshs.changes    2026-05-28 
17:29:26.201921844 +0200
@@ -1,0 +2,9 @@
+Wed May 27 18:56:18 UTC 2026 - Martin Hauke <[email protected]>
+
+- Update to version 2.0.9
+ * Add FTP/SFTP.
+ * Fix insecure auth options in FTP Server.
+ * Fix timing attack surface on ftp password comparison.
+ * Fixed entropy on cert generation in Cert Serial Number.
+
+-------------------------------------------------------------------

Old:
----
  goshs-2.0.8.tar.gz

New:
----
  goshs-2.0.9.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ goshs.spec ++++++
--- /var/tmp/diff_new_pack.tQdOhv/_old  2026-05-28 17:29:27.077958107 +0200
+++ /var/tmp/diff_new_pack.tQdOhv/_new  2026-05-28 17:29:27.081958273 +0200
@@ -16,7 +16,7 @@
 #
 
 Name:           goshs
-Version:        2.0.8
+Version:        2.0.9
 Release:        0
 Summary:        A simple HTTP server
 License:        MIT

++++++ goshs-2.0.8.tar.gz -> goshs-2.0.9.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/goshs-2.0.8/.github/workflows/codecov.yml 
new/goshs-2.0.9/.github/workflows/codecov.yml
--- old/goshs-2.0.8/.github/workflows/codecov.yml       2026-05-13 
15:57:24.000000000 +0200
+++ new/goshs-2.0.9/.github/workflows/codecov.yml       2026-05-27 
15:49:51.000000000 +0200
@@ -10,7 +10,7 @@
 
     steps:
       - name: Checkout repository
-        uses: actions/checkout@v6
+        uses: actions/[email protected]
         with:
           fetch-depth: 0
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/goshs-2.0.8/.github/workflows/codeql-analysis.yml 
new/goshs-2.0.9/.github/workflows/codeql-analysis.yml
--- old/goshs-2.0.8/.github/workflows/codeql-analysis.yml       2026-05-13 
15:57:24.000000000 +0200
+++ new/goshs-2.0.9/.github/workflows/codeql-analysis.yml       2026-05-27 
15:49:51.000000000 +0200
@@ -44,7 +44,7 @@
 
     # Initializes the CodeQL tools for scanning.
     - name: Initialize CodeQL
-      uses: github/codeql-action/init@e46ed2cbd01164d986452f91f178727624ae40d7 
# v3
+      uses: github/codeql-action/init@9e0d7b8d25671d64c341c19c0152d693099fb5ba 
# v3
       with:
         languages: ${{ matrix.language }}
         # If you wish to specify custom queries, you can do so here or in a 
config file.
@@ -55,7 +55,7 @@
     # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
     # If this step fails, then you should remove it and run the build manually 
(see below)
     - name: Autobuild
-      uses: 
github/codeql-action/autobuild@e46ed2cbd01164d986452f91f178727624ae40d7 # v3
+      uses: 
github/codeql-action/autobuild@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v3
 
     # â„šī¸ Command-line programs to run using the OS shell.
     # 📚 https://git.io/JvXDl
@@ -69,4 +69,4 @@
     #   make release
 
     - name: Perform CodeQL Analysis
-      uses: 
github/codeql-action/analyze@e46ed2cbd01164d986452f91f178727624ae40d7 # v3
+      uses: 
github/codeql-action/analyze@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v3
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/goshs-2.0.8/.github/workflows/release.yml 
new/goshs-2.0.9/.github/workflows/release.yml
--- old/goshs-2.0.8/.github/workflows/release.yml       2026-05-13 
15:57:24.000000000 +0200
+++ new/goshs-2.0.9/.github/workflows/release.yml       2026-05-27 
15:49:51.000000000 +0200
@@ -29,7 +29,7 @@
           choco --version
 
       - name: Run GoReleaser
-        uses: 
goreleaser/goreleaser-action@1a80836c5c9d9e5755a25cb59ec6f45a3b5f41a8 # v7.2.1
+        uses: 
goreleaser/goreleaser-action@5daf1e915a5f0af01ddbcd89a43b8061ff4f1a89 # v7.2.2
         with:
           distribution: goreleaser
           version: latest
@@ -43,13 +43,15 @@
     needs: goreleaser
     runs-on: ubuntu-latest
     if: startsWith(github.ref, 'refs/tags/')
+    continue-on-error: true
     steps:
       - name: Trigger COPR build
         env:
           COPR_LOGIN: ${{ secrets.COPR_LOGIN }}
           COPR_TOKEN: ${{ secrets.COPR_TOKEN }}
         run: |
-          curl -sf -X POST \
+          curl -sf --retry 5 --retry-delay 10 --retry-all-errors \
+            -X POST \
             --user "${COPR_LOGIN}:${COPR_TOKEN}" \
             -F "ownername=patrickhener" \
             -F "projectname=goshs" \
@@ -58,4 +60,4 @@
             -F "committish=${{ github.ref_name }}" \
             -F "spec=packaging/rpm/goshs.spec" \
             -F "srpm_build_method=rpkg" \
-            "https://copr.fedorainfrastructure.org/api_3/build/create/scm";
+            "https://copr.fedorainfracloud.org/api_3/build/create/scm";
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/goshs-2.0.8/.github/workflows/snap.yml 
new/goshs-2.0.9/.github/workflows/snap.yml
--- old/goshs-2.0.8/.github/workflows/snap.yml  2026-05-13 15:57:24.000000000 
+0200
+++ new/goshs-2.0.9/.github/workflows/snap.yml  2026-05-27 15:49:51.000000000 
+0200
@@ -20,7 +20,7 @@
 
     steps:
       - name: Checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 
v4.2.2
+        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 
v6.0.2
         with:
           fetch-depth: 0
 
@@ -29,7 +29,7 @@
         id: build
 
       - name: Upload snap artifact
-        uses: actions/upload-artifact@v4
+        uses: actions/upload-artifact@v7
         with:
           name: snap-${{ matrix.arch }}
           path: ${{ steps.build.outputs.snap }}
@@ -40,7 +40,7 @@
 
     steps:
       - name: Download snap artifacts
-        uses: actions/download-artifact@v4
+        uses: actions/download-artifact@v8
         with:
           pattern: snap-*
           merge-multiple: true
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/goshs-2.0.8/README.md new/goshs-2.0.9/README.md
--- old/goshs-2.0.8/README.md   2026-05-13 15:57:24.000000000 +0200
+++ new/goshs-2.0.9/README.md   2026-05-27 15:49:51.000000000 +0200
@@ -1,4 +1,4 @@
-![Version](https://img.shields.io/badge/Version-v2.0.8-green)
+![Version](https://img.shields.io/badge/Version-v2.0.9-green)
 
[![GitHub](https://img.shields.io/github/license/patrickhener/goshs)](https://github.com/patrickhener/goshs/blob/master/LICENSE)
 ![GitHub go.mod Go 
version](https://img.shields.io/github/go-mod/go-version/patrickhener/goshs)
 [![GitHub 
issues](https://img.shields.io/github/issues-raw/patrickhener/goshs)](https://github.com/patrickhener/goshs/issues)
@@ -11,7 +11,7 @@
 
 You're mid-engagement. You need to transfer a file, catch an SMB hash, or 
stand up a quick HTTPS server — and `python3 -m http.server` won't cut it.
 
-**goshs** is a single-binary file server built for the moments when you need 
more than Python's SimpleHTTPServer but don't want to configure Apache. HTTP/S, 
WebDAV, SFTP, SMB, LDAP/S, basic auth, share links, DNS/SMTP callbacks, NTLM 
hash capture + cracking — all from one command.
+**goshs** is a single-binary file server built for the moments when you need 
more than Python's SimpleHTTPServer but don't want to configure Apache. HTTP/S, 
WebDAV, FTP/SFTP, SMB, LDAP/S, basic auth, share links, DNS/SMTP callbacks, 
NTLM hash capture + cracking — all from one command.
 
 
![intro](https://github.com/patrickhener/image-cdn/blob/main/goshs.gif?raw=true)
 
@@ -53,7 +53,7 @@
 | | |
 |---|---|
 | 📁 **File Operations** | Download, upload (drag & drop, POST/PUT), delete, 
bulk ZIP, QR codes |
-| 🔌 **Protocols** | HTTP/S, WebDAV, SFTP, SMB, LDAP/S |
+| 🔌 **Protocols** | HTTP/S, WebDAV, FTP/SFTP, SMB, LDAP/S |
 | 🔒 **Auth & Security** | Basic auth, certificate auth, TLS (self-signed, 
Let's Encrypt, custom cert), IP whitelist, file-based ACLs |
 | âš™ī¸ **Server Modes** | Read-only, upload-only, no-delete, silent, invisible, 
CLI command execution |
 | 🔗 **Share Links** | Token-based sharing, download limit, time limit |
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/goshs-2.0.8/ca/ca.go new/goshs-2.0.9/ca/ca.go
--- old/goshs-2.0.8/ca/ca.go    2026-05-13 15:57:24.000000000 +0200
+++ new/goshs-2.0.9/ca/ca.go    2026-05-27 15:49:51.000000000 +0200
@@ -27,7 +27,7 @@
 )
 
 func randomSerial() (big.Int, error) {
-       n, err := rand.Int(rand.Reader, big.NewInt(1000))
+       n, err := rand.Int(rand.Reader, big.NewInt(1).Lsh(big.NewInt(1), 128))
        if err != nil {
                return *big.NewInt(0), err
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/goshs-2.0.8/config/config.go 
new/goshs-2.0.9/config/config.go
--- old/goshs-2.0.8/config/config.go    2026-05-13 15:57:24.000000000 +0200
+++ new/goshs-2.0.9/config/config.go    2026-05-27 15:49:51.000000000 +0200
@@ -59,10 +59,6 @@
        WebhookURL          string   `json:"webhook_url"`
        WebhookProvider     string   `json:"webhook_provider"`
        WebhookEvents       []string `json:"webhook_events"`
-       SFTP                bool     `json:"sftp"`
-       SFTPPort            int      `json:"sftp_port"`
-       SFTPKeyFile         string   `json:"sftp_keyfile"`
-       SFTPHostKeyFile     string   `json:"sftp_host_keyfile"`
        Whitelist           string   `json:"whitelist"`
        TrustedProxies      string   `json:"trusted_proxies"`
        Tunnel              bool     `json:"tunnel"`
@@ -84,6 +80,11 @@
        LDAPJNDIEnabled     bool     `json:"ldap_jndi"`
        LDAPJNDIBase        string   `json:"ldap_jndi_base"`
        LDAPWordlist        string   `json:"ldap_wordlist"`
+       FTP                 bool     `json:"ftp"`
+       FTPPort             int      `json:"ftp_port"`
+       FTPSFTPMode         bool     `json:"ftp_sftp_mode"`
+       FTPKeyFile          string   `json:"ftp_keyfile"`
+       FTPHostKeyFile      string   `json:"ftp_host_keyfile"`
 }
 
 func LoadConfig(opts *options.Options) (*options.Options, error) {
@@ -140,10 +141,6 @@
        opts.WebhookURL = cfg.WebhookURL
        opts.WebhookProvider = cfg.WebhookProvider
        opts.WebhookEventsParsed = cfg.WebhookEvents
-       opts.SFTP = cfg.SFTP
-       opts.SFTPPort = cfg.SFTPPort
-       opts.SFTPKeyFile = cfg.SFTPKeyFile
-       opts.SFTPHostKeyFile = cfg.SFTPHostKeyFile
        opts.Whitelist = cfg.Whitelist
        opts.TrustedProxies = cfg.TrustedProxies
        opts.Invisible = cfg.Invisible
@@ -165,6 +162,11 @@
        opts.LDAPPort = cfg.LDAPPort
        opts.LDAPJNDIEnabled = cfg.LDAPJNDIEnabled
        opts.LDAPJNDIBase = cfg.LDAPJNDIBase
+       opts.FTP = cfg.FTP
+       opts.FTPPort = cfg.FTPPort
+       opts.FTPSFTPMode = cfg.FTPSFTPMode
+       opts.FTPKeyFile = cfg.FTPKeyFile
+       opts.FTPHostKeyFile = cfg.FTPHostKeyFile
 
        // Default upload folder to webroot if not set in config
        if opts.UploadFolder == "" {
@@ -211,10 +213,6 @@
                WebhookURL:          "",
                WebhookProvider:     "discord",
                WebhookEvents:       []string{"all"},
-               SFTP:                false,
-               SFTPPort:            2022,
-               SFTPKeyFile:         "",
-               SFTPHostKeyFile:     "",
                Whitelist:           "",
                TrustedProxies:      "",
                Tunnel:              false,
@@ -235,6 +233,11 @@
                LDAPPort:            389,
                LDAPJNDIEnabled:     false,
                LDAPJNDIBase:        "",
+               FTP:                 false,
+               FTPPort:             2121,
+               FTPSFTPMode:         false,
+               FTPKeyFile:          "",
+               FTPHostKeyFile:      "",
        }
 
        b, err := json.MarshalIndent(defaultConfig, "", "  ")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/goshs-2.0.8/config/config_test.go 
new/goshs-2.0.9/config/config_test.go
--- old/goshs-2.0.8/config/config_test.go       2026-05-13 15:57:24.000000000 
+0200
+++ new/goshs-2.0.9/config/config_test.go       2026-05-27 15:49:51.000000000 
+0200
@@ -194,21 +194,23 @@
        require.Equal(t, "10.0.0.1", result.DNSIP)
 }
 
-func TestLoadConfig_SFTPFields(t *testing.T) {
+func TestLoadConfig_FTPSFTPFields(t *testing.T) {
        cfg := Config{
-               SFTP:            true,
-               SFTPPort:        2022,
-               SFTPKeyFile:     "/etc/goshs/authorized_keys",
-               SFTPHostKeyFile: "/etc/goshs/host_key",
+               FTP:            true,
+               FTPPort:        2022,
+               FTPSFTPMode:    true,
+               FTPKeyFile:     "/etc/goshs/authorized_keys",
+               FTPHostKeyFile: "/etc/goshs/host_key",
        }
        path := writeTempConfig(t, cfg)
        opts := &options.Options{ConfigFile: path}
        result, err := LoadConfig(opts)
        require.NoError(t, err)
-       require.True(t, result.SFTP)
-       require.Equal(t, 2022, result.SFTPPort)
-       require.Equal(t, "/etc/goshs/authorized_keys", result.SFTPKeyFile)
-       require.Equal(t, "/etc/goshs/host_key", result.SFTPHostKeyFile)
+       require.True(t, result.FTP)
+       require.Equal(t, 2022, result.FTPPort)
+       require.True(t, result.FTPSFTPMode)
+       require.Equal(t, "/etc/goshs/authorized_keys", result.FTPKeyFile)
+       require.Equal(t, "/etc/goshs/host_key", result.FTPHostKeyFile)
 }
 
 func TestLoadConfig_TLSFields(t *testing.T) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/goshs-2.0.8/example/goshs.json.example 
new/goshs-2.0.9/example/goshs.json.example
--- old/goshs-2.0.8/example/goshs.json.example  2026-05-13 15:57:24.000000000 
+0200
+++ new/goshs-2.0.9/example/goshs.json.example  2026-05-27 15:49:51.000000000 
+0200
@@ -36,15 +36,16 @@
   "webhook_events": [
     "all"
   ],
-  "sftp": false,
-  "sftp_port": 2022,
-  "sftp_keyfile": "",
-  "sftp_host_keyfile": "",
+  "ftp": false,
+  "ftp_port": 2121,
+  "ftp_sftp_mode": false,
+  "ftp_keyfile": "",
+  "ftp_host_keyfile": "",
   "whitelist": "",
   "trusted_proxies": "",
   "tunnel": false,
   "dns_server": false,
-  "dns_port": 8053
+  "dns_port": 8053,
   "dns_ip": "127.0.0.1",
   "smtp_server": false,
   "smtp_port": 2525,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/goshs-2.0.8/ftpserver/ftpserver.go 
new/goshs-2.0.9/ftpserver/ftpserver.go
--- old/goshs-2.0.8/ftpserver/ftpserver.go      1970-01-01 01:00:00.000000000 
+0100
+++ new/goshs-2.0.9/ftpserver/ftpserver.go      2026-05-27 15:49:51.000000000 
+0200
@@ -0,0 +1,146 @@
+package ftpserver
+
+import (
+       "crypto/subtle"
+       "crypto/tls"
+       "fmt"
+       "net"
+       "strconv"
+
+       ftplib "github.com/fclairamb/ftpserverlib"
+       "github.com/spf13/afero"
+       "goshs.de/goshs/v2/httpserver"
+       "goshs.de/goshs/v2/logger"
+       "goshs.de/goshs/v2/options"
+       "goshs.de/goshs/v2/webhook"
+)
+
+type FTPServer struct {
+       IP        string
+       Port      int
+       Root      string
+       Username  string
+       Password  string
+       ReadOnly  bool
+       NoDelete  bool
+       Webhook   webhook.Webhook
+       Whitelist *httpserver.Whitelist
+}
+
+func NewFTPServer(opts *options.Options, wl *httpserver.Whitelist, wh 
webhook.Webhook) *FTPServer {
+       return &FTPServer{
+               IP:        opts.IP,
+               Port:      opts.FTPPort,
+               Root:      opts.Webroot,
+               Username:  opts.Username,
+               Password:  opts.Password,
+               ReadOnly:  opts.ReadOnly,
+               NoDelete:  opts.NoDelete,
+               Webhook:   wh,
+               Whitelist: wl,
+       }
+}
+
+func (s *FTPServer) Start() error {
+       driver := &mainDriver{srv: s}
+       srv := ftplib.NewFtpServer(driver)
+       logger.Infof("Starting FTP server on %s:%d", s.IP, s.Port)
+       return srv.ListenAndServe()
+}
+
+func (s *FTPServer) HandleWebhookSend(action, path, ip string, blocked bool) {
+       var message string
+       if blocked {
+               message = fmt.Sprintf("[FTP] BLOCKED %s - [%s] - \"%s\"", ip, 
action, path)
+       } else {
+               message = fmt.Sprintf("[FTP] %s - [%s] - \"%s\"", ip, action, 
path)
+       }
+       logger.HandleWebhookSend(message, "ftp", s.Webhook)
+}
+
+// mainDriver implements ftplib.MainDriver
+type mainDriver struct {
+       srv *FTPServer
+}
+
+func (d *mainDriver) GetSettings() (*ftplib.Settings, error) {
+       return &ftplib.Settings{
+               ListenAddr:              net.JoinHostPort(d.srv.IP, 
strconv.Itoa(d.srv.Port)),
+               Banner:                  "goshs FTP server ready",
+               ActiveTransferPortNon20: true,
+       }, nil
+}
+
+func (d *mainDriver) ClientConnected(cc ftplib.ClientContext) (string, error) {
+       clientIP := cc.RemoteAddr().String()
+       if !isAllowedIP(cc.RemoteAddr(), d.srv.Whitelist) {
+               logger.Warnf("[FTP] [WHITELIST] Access denied for %s", clientIP)
+               return "", fmt.Errorf("access denied")
+       }
+       logger.Infof("[FTP] Client connected from %s", clientIP)
+       return "goshs FTP server", nil
+}
+
+func (d *mainDriver) ClientDisconnected(cc ftplib.ClientContext) {
+       logger.Infof("[FTP] Client disconnected: %s", cc.RemoteAddr())
+}
+
+func (d *mainDriver) AuthUser(cc ftplib.ClientContext, user, pass string) 
(ftplib.ClientDriver, error) {
+       if d.srv.Username != "" || d.srv.Password != "" {
+               userOK := subtle.ConstantTimeCompare([]byte(user), 
[]byte(d.srv.Username)) == 1
+               passOK := subtle.ConstantTimeCompare([]byte(pass), 
[]byte(d.srv.Password)) == 1
+               if !userOK || !passOK {
+                       logger.Warnf("[FTP] Auth failed for user '%s' from %s", 
user, cc.RemoteAddr())
+                       d.srv.HandleWebhookSend("AUTH", user, 
cc.RemoteAddr().String(), true)
+                       return nil, fmt.Errorf("invalid credentials")
+               }
+       }
+       logger.Infof("[FTP] User '%s' authenticated from %s", user, 
cc.RemoteAddr())
+       d.srv.HandleWebhookSend("AUTH", user, cc.RemoteAddr().String(), false)
+
+       base := afero.NewBasePathFs(afero.NewOsFs(), d.srv.Root)
+       if d.srv.ReadOnly {
+               return afero.NewReadOnlyFs(base), nil
+       }
+       if d.srv.NoDelete {
+               return &noDeleteFs{Fs: base}, nil
+       }
+       return base, nil
+}
+
+func (d *mainDriver) GetTLSConfig() (*tls.Config, error) {
+       return nil, nil
+}
+
+// noDeleteFs wraps afero.Fs and blocks remove operations.
+type noDeleteFs struct {
+       afero.Fs
+}
+
+func (fs *noDeleteFs) Remove(name string) error {
+       return fmt.Errorf("delete not allowed")
+}
+
+func (fs *noDeleteFs) RemoveAll(path string) error {
+       return fmt.Errorf("delete not allowed")
+}
+
+func isAllowedIP(addr net.Addr, wl *httpserver.Whitelist) bool {
+       if !wl.Enabled {
+               return true
+       }
+       host, _, err := net.SplitHostPort(addr.String())
+       if err != nil {
+               host = addr.String()
+       }
+       ip := net.ParseIP(host)
+       if ip == nil {
+               return false
+       }
+       for _, n := range wl.Networks {
+               if n.Contains(ip) {
+                       return true
+               }
+       }
+       return false
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/goshs-2.0.8/go.mod new/goshs-2.0.9/go.mod
--- old/goshs-2.0.8/go.mod      2026-05-13 15:57:24.000000000 +0200
+++ new/goshs-2.0.9/go.mod      2026-05-27 15:49:51.000000000 +0200
@@ -56,6 +56,7 @@
        github.com/docker/go-units v0.5.0 // indirect
        github.com/ebitengine/purego v0.8.2 // indirect
        github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 // 
indirect
+       github.com/fclairamb/ftpserverlib v0.31.0 // indirect
        github.com/felixge/httpsnoop v1.0.4 // indirect
        github.com/geoffgarside/ber v1.1.0 // indirect
        github.com/go-jose/go-jose/v4 v4.1.4 // indirect
@@ -89,6 +90,7 @@
        github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // 
indirect
        github.com/rivo/uniseg v0.4.7 // indirect
        github.com/shirou/gopsutil/v4 v4.25.1 // indirect
+       github.com/spf13/afero v1.15.0 // indirect
        github.com/tklauser/go-sysconf v0.3.12 // indirect
        github.com/tklauser/numcpus v0.6.1 // indirect
        github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
@@ -104,7 +106,7 @@
        go.opentelemetry.io/proto/otlp v1.6.0 // indirect
        golang.org/x/mod v0.35.0 // indirect
        golang.org/x/sync v0.20.0 // indirect
-       golang.org/x/sys v0.43.0 // indirect
+       golang.org/x/sys v0.44.0 // indirect
        golang.org/x/term v0.42.0 // indirect
        golang.org/x/tools v0.44.0 // indirect
        gopkg.in/yaml.v3 v3.0.1 // indirect
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/goshs-2.0.8/go.sum new/goshs-2.0.9/go.sum
--- old/goshs-2.0.8/go.sum      2026-05-13 15:57:24.000000000 +0200
+++ new/goshs-2.0.9/go.sum      2026-05-27 15:49:51.000000000 +0200
@@ -74,6 +74,8 @@
 github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6/go.mod 
h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
 github.com/emersion/go-smtp v0.24.0 
h1:g6AfoF140mvW0vLNPD/LuCBLEAdlxOjIXqbIkJIS6Wk=
 github.com/emersion/go-smtp v0.24.0/go.mod 
h1:ZtRRkbTyp2XTHCA+BmyTFTrj8xY4I+b4McvHxCU2gsQ=
+github.com/fclairamb/ftpserverlib v0.31.0 
h1:ws4fLJvuU/mgg1uIgRjCJfc6CrFSgIBAOxVsnawXegE=
+github.com/fclairamb/ftpserverlib v0.31.0/go.mod 
h1:C5hUT0sd9P5XsHMxsmpcrT6wdZexu3qtUu1z0Ih6aG0=
 github.com/felixge/httpsnoop v1.0.4 
h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
 github.com/felixge/httpsnoop v1.0.4/go.mod 
h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
 github.com/geoffgarside/ber v1.1.0 
h1:qTmFG4jJbwiSzSXoNJeHcOprVzZ8Ulde2Rrrifu5U9w=
@@ -186,6 +188,8 @@
 github.com/sirupsen/logrus v1.9.3/go.mod 
h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e 
h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod 
h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
+github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
+github.com/spf13/afero v1.15.0/go.mod 
h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
 github.com/stretchr/objx v0.1.0/go.mod 
h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.4.0/go.mod 
h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
 github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4=
@@ -304,6 +308,8 @@
 golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
 golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
+golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
 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=
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/goshs-2.0.8/goshsversion/version.go 
new/goshs-2.0.9/goshsversion/version.go
--- old/goshs-2.0.8/goshsversion/version.go     2026-05-13 15:57:24.000000000 
+0200
+++ new/goshs-2.0.9/goshsversion/version.go     2026-05-27 15:49:51.000000000 
+0200
@@ -1,3 +1,3 @@
 package goshsversion
 
-var GoshsVersion = "v2.0.8"
+var GoshsVersion = "v2.0.9"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/goshs-2.0.8/options/options.go 
new/goshs-2.0.9/options/options.go
--- old/goshs-2.0.8/options/options.go  2026-05-13 15:57:24.000000000 +0200
+++ new/goshs-2.0.9/options/options.go  2026-05-27 15:49:51.000000000 +0200
@@ -34,10 +34,6 @@
        CertAuth            string   // ""
        WebDav              bool     // false
        WebDavPort          int      // 8001
-       SFTP                bool     // false
-       SFTPPort            int      // 2022
-       SFTPKeyFile         string   // ""
-       SFTPHostKeyFile     string   // ""
        UploadOnly          bool     // false
        ReadOnly            bool     // false
        NoClipboard         bool     // false
@@ -81,6 +77,11 @@
        LDAPJNDIEnabled     bool     // false — when true, use search baseDN as 
class name
        LDAPJNDIBase        string   // "" auto-constructs from IP/port
        LDAPWordlist        string   // "" optional wordlist path for NTLM hash 
cracking
+       FTP                 bool     // false
+       FTPPort             int      // 2121
+       FTPSFTPMode         bool     // false
+       FTPKeyFile          string   // ""
+       FTPHostKeyFile      string   // ""
 }
 
 func Parse() (*Options, bool) {
@@ -152,13 +153,6 @@
        flag.StringVar(&opts.WebhookEvents, "webhook-events", "all", "")
        flag.StringVar(&opts.WebhookProvider, "Wp", "Discord", "")
        flag.StringVar(&opts.WebhookProvider, "webhook-provider", "Discord", "")
-       flag.BoolVar(&opts.SFTP, "sftp", false, "sftp")
-       flag.IntVar(&opts.SFTPPort, "sp", 2022, "sftp port")
-       flag.IntVar(&opts.SFTPPort, "sftp-port", 2022, "sftp port")
-       flag.StringVar(&opts.SFTPKeyFile, "skf", "", "")
-       flag.StringVar(&opts.SFTPKeyFile, "sftp-keyfile", "", "")
-       flag.StringVar(&opts.SFTPHostKeyFile, "shk", "", "")
-       flag.StringVar(&opts.SFTPHostKeyFile, "sftp-host-keyfile", "", "")
        flag.StringVar(&opts.Whitelist, "ipw", "", "")
        flag.StringVar(&opts.Whitelist, "ip-whitelist", "", "")
        flag.StringVar(&opts.TrustedProxies, "tpw", "", "")
@@ -195,6 +189,13 @@
        flag.BoolVar(&opts.LDAPJNDIEnabled, "ldap-jndi", false, "Enable dynamic 
JNDI mode (baseDN becomes the class name)")
        flag.StringVar(&opts.LDAPJNDIBase, "ldap-jndi-base", "", "JNDI codeBase 
URL override (default: auto from HTTP server)")
        flag.StringVar(&opts.LDAPWordlist, "ldap-wordlist", "", "Wordlist file 
for LDAP NTLM hash cracking")
+       flag.BoolVar(&opts.FTP, "ftp", false, "Enable FTP server")
+       flag.IntVar(&opts.FTPPort, "ftp-port", 2121, "FTP server port")
+       flag.BoolVar(&opts.FTPSFTPMode, "ftp-sftp", false, "Use SFTP instead of 
plain FTP")
+       flag.StringVar(&opts.FTPKeyFile, "fkf", "", "")
+       flag.StringVar(&opts.FTPKeyFile, "ftp-keyfile", "", "")
+       flag.StringVar(&opts.FTPHostKeyFile, "fhk", "", "")
+       flag.StringVar(&opts.FTPHostKeyFile, "ftp-host-keyfile", "", "")
 
        // One-shot flags
        upd := flag.Bool("update", false, "update")
@@ -270,11 +271,12 @@
   -slh,   --le-http       Port to use for Let's Encrypt HTTP Challenge    
(default: 80)
   -slt,   --le-tls        Port to use for Let's Encrypt TLS ALPN Challenge 
(default: 443)
 
-SFTP server options:
-  -sftp                        Activate SFTP server capabilities (default: 
false)
-  -sp,    --sftp-port          The port SFTP listens on          (default: 
2022)
-  -skf,   --sftp-keyfile       Authorized_keys file for pubkey auth
-  -shk,   --sftp-host-keyfile  SSH Host key file for identification
+FTP/SFTP server options:
+  -ftp                          Activate FTP server capabilities          
(default: false)
+  -ftp-port                     The port FTP/SFTP listens on              
(default: 2121)
+  -ftp-sftp                     Switch to SFTP instead of plain FTP       
(default: false)
+  -fkf, --ftp-keyfile           Authorized_keys file for SFTP pubkey auth
+  -fhk, --ftp-host-keyfile      SSH host key file for SFTP identification
 
 SMB server options:
   -smb                        Activate SMB server capabilities         
(default: false)
@@ -314,7 +316,7 @@
   -Wu, --webhook-url        URL to send webhook requests to
   -We, --webhook-events     Comma separated list of events to notify
                             [all, upload, delete, download, view, webdav,
-                            sftp, smb, dns, smtp, redirect, verbose]   
(default: all)
+                            ftp, smb, dns, smtp, redirect, verbose]        
(default: all)
   -Wp, --webhook-provider   Webhook provider
                             [Discord, Mattermost, Slack]                
(default: Discord)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/goshs-2.0.8/packaging/rpm/goshs.spec 
new/goshs-2.0.9/packaging/rpm/goshs.spec
--- old/goshs-2.0.8/packaging/rpm/goshs.spec    2026-05-13 15:57:24.000000000 
+0200
+++ new/goshs-2.0.9/packaging/rpm/goshs.spec    2026-05-27 15:49:51.000000000 
+0200
@@ -1,5 +1,5 @@
 Name:           goshs
-Version:        2.0.8
+Version:        2.0.9
 Release:        1%{?dist}
 Summary:        Beyond Python's http.server — single-binary file server for 
pentesters
 
@@ -7,7 +7,7 @@
 URL:            https://github.com/patrickhener/goshs
 Source0:        
%{url}/archive/refs/tags/v%{version}.tar.gz#/%{name}-%{version}.tar.gz
 
-BuildRequires:  golang >= 1.26
+BuildRequires:  golang
 
 %description
 A single-binary file server for pentesters, CTF players, and sysadmins.
@@ -40,7 +40,9 @@
 %{_bindir}/%{name}
 
 %changelog
-* Tue May 13 2026 Patrick Hener <[email protected]> - 2.0.8-1
+* Wed May 27 2026 Patrick Hener <[email protected]> - 2.0.9-1
+- Add new version v2.0.9
+* Wed May 13 2026 Patrick Hener <[email protected]> - 2.0.8-1
 - Add more packaging
-* Tue May 13 2026 Patrick Hener <[email protected]> - 2.0.7-1
+* Wed May 13 2026 Patrick Hener <[email protected]> - 2.0.7-1
 - Initial COPR package
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/goshs-2.0.8/sanity/checks.go 
new/goshs-2.0.9/sanity/checks.go
--- old/goshs-2.0.8/sanity/checks.go    2026-05-13 15:57:24.000000000 +0200
+++ new/goshs-2.0.9/sanity/checks.go    2026-05-27 15:49:51.000000000 +0200
@@ -51,14 +51,14 @@
        }
        // Sanity check for invisible mode
        if opts.Invisible {
-               opts.SFTP = false
                opts.WebDav = false
                opts.MDNS = false
                opts.Silent = false
                opts.DNS = false
                opts.SMTP = false
                opts.LDAP = false
-               logger.Warn("Invisible mode activated, disabling SFTP, WebDAV, 
silent mode, DNS, SMTP, LDAP and mDNS support")
+               opts.FTP = false
+               logger.Warn("Invisible mode activated, disabling FTP/SFTP, 
WebDAV, silent mode, DNS, SMTP, LDAP and mDNS support")
        }
 
        // Sanity check for upload only vs read only
@@ -85,13 +85,13 @@
                logger.Fatal("To use certificate based authentication with a CA 
cert you will need tls in any mode (-ss, -sk/-sc, -p12, -sl)")
        }
 
-       // Sanity check either user:pass or keyfile when using sftp
-       if opts.SFTP && (opts.BasicAuth == "" && opts.SFTPKeyFile == "") {
-               logger.Fatal("When using SFTP you need to either specify an 
authorized keyfile using -sfk or username and password using -b")
+       // Sanity check either user:pass or keyfile when using sftp mode
+       if opts.FTP && opts.FTPSFTPMode && (opts.BasicAuth == "" && 
opts.FTPKeyFile == "") {
+               logger.Fatal("When using SFTP you need to either specify an 
authorized keyfile using -fkf or username and password using -b")
        }
 
        // Sanity check: empty username is not valid for SFTP password auth
-       if opts.SFTP && strings.HasPrefix(opts.BasicAuth, ":") {
+       if opts.FTP && opts.FTPSFTPMode && strings.HasPrefix(opts.BasicAuth, 
":") {
                logger.Fatal("When using SFTP with password authentication, the 
username cannot be empty. Please provide a non-empty username with -b 
'user:pass'.")
        }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/goshs-2.0.8/sanity/checks_test.go 
new/goshs-2.0.9/sanity/checks_test.go
--- old/goshs-2.0.8/sanity/checks_test.go       2026-05-13 15:57:24.000000000 
+0200
+++ new/goshs-2.0.9/sanity/checks_test.go       2026-05-27 15:49:51.000000000 
+0200
@@ -19,17 +19,18 @@
 
 func TestCheck_InvisibleDisablesFeatures(t *testing.T) {
        opts := &options.Options{
-               Invisible: true,
-               SFTP:     true,
-               WebDav:   true,
-               MDNS:     true,
-               Silent:   true,
-               DNS:      true,
-               SMTP:     true,
+               Invisible:   true,
+               FTP:         true,
+               FTPSFTPMode: true,
+               WebDav:      true,
+               MDNS:        true,
+               Silent:      true,
+               DNS:         true,
+               SMTP:        true,
        }
        result, err := Check(opts)
        require.NoError(t, err)
-       require.False(t, result.SFTP)
+       require.False(t, result.FTP)
        require.False(t, result.WebDav)
        require.False(t, result.MDNS)
        require.False(t, result.Silent)
@@ -206,7 +207,7 @@
 func TestCheck_InvisibleWithSMBOnly(t *testing.T) {
        opts := &options.Options{
                Invisible: true,
-               SFTP:      false,
+               FTP:       false,
                WebDav:    false,
        }
        result, err := Check(opts)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/goshs-2.0.8/server/server.go 
new/goshs-2.0.9/server/server.go
--- old/goshs-2.0.8/server/server.go    2026-05-13 15:57:24.000000000 +0200
+++ new/goshs-2.0.9/server/server.go    2026-05-27 15:49:51.000000000 +0200
@@ -5,6 +5,7 @@
 
        "goshs.de/goshs/v2/clipboard"
        "goshs.de/goshs/v2/dnsserver"
+       "goshs.de/goshs/v2/ftpserver"
        "goshs.de/goshs/v2/httpserver"
        "goshs.de/goshs/v2/ldapserver"
        "goshs.de/goshs/v2/logger"
@@ -38,11 +39,6 @@
                go webdavSrv.Start("webdav")
        }
 
-       if opts.SFTP {
-               sftpSrv := sftpserver.NewSFTPServer(opts, wl, *wh)
-               go sftpSrv.Start()
-       }
-
        if opts.DNS {
                dnsSrv := dnsserver.NewDNSServer(opts, hub, wh)
                go dnsSrv.Start()
@@ -63,9 +59,19 @@
                go ldapSrv.Start()
        }
 
+       if opts.FTP {
+               if opts.FTPSFTPMode {
+                       sftpSrv := sftpserver.NewSFTPServer(opts, wl, *wh)
+                       go sftpSrv.Start()
+               } else {
+                       ftpSrv := ftpserver.NewFTPServer(opts, wl, *wh)
+                       go ftpSrv.Start()
+               }
+       }
+
        // Zeroconf mDNS
        if opts.MDNS {
-               err := utils.RegisterZeroconfMDNS(opts.SSL, opts.Port, 
opts.WebDav, opts.WebDavPort, opts.SFTP, opts.SFTPPort, opts.SMTP, 
opts.SMTPPort, opts.DNS, opts.DNSPort, opts.SMB, opts.SMBPort, opts.LDAP, 
opts.LDAPPort)
+               err := utils.RegisterZeroconfMDNS(opts.SSL, opts.Port, 
opts.WebDav, opts.WebDavPort, opts.SMTP, opts.SMTPPort, opts.DNS, opts.DNSPort, 
opts.SMB, opts.SMBPort, opts.LDAP, opts.LDAPPort, opts.FTP, opts.FTPSFTPMode, 
opts.FTPPort)
                if err != nil {
                        logger.Warnf("error registering zeroconf mDNS: %+v", 
err)
                }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/goshs-2.0.8/sftpserver/handler_test.go 
new/goshs-2.0.9/sftpserver/handler_test.go
--- old/goshs-2.0.8/sftpserver/handler_test.go  2026-05-13 15:57:24.000000000 
+0200
+++ new/goshs-2.0.9/sftpserver/handler_test.go  2026-05-27 15:49:51.000000000 
+0200
@@ -306,15 +306,15 @@
 
 func TestNewSFTPServer(t *testing.T) {
        opts := &options.Options{
-               IP:              "127.0.0.1",
-               SFTPPort:        2222,
-               SFTPKeyFile:     "keys",
-               Username:        "user",
-               Password:        "pass",
-               Webroot:         "/tmp",
-               ReadOnly:        true,
-               UploadOnly:      false,
-               SFTPHostKeyFile: "hostkey",
+               IP:             "127.0.0.1",
+               FTPPort:        2222,
+               FTPKeyFile:     "keys",
+               Username:       "user",
+               Password:       "pass",
+               Webroot:        "/tmp",
+               ReadOnly:       true,
+               UploadOnly:     false,
+               FTPHostKeyFile: "hostkey",
        }
        wh := webhook.Register(false, "", "discord", []string{})
        wl := &httpserver.Whitelist{}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/goshs-2.0.8/sftpserver/helper.go 
new/goshs-2.0.9/sftpserver/helper.go
--- old/goshs-2.0.8/sftpserver/helper.go        2026-05-13 15:57:24.000000000 
+0200
+++ new/goshs-2.0.9/sftpserver/helper.go        2026-05-27 15:49:51.000000000 
+0200
@@ -13,10 +13,10 @@
        "runtime"
        "strings"
 
-       "goshs.de/goshs/v2/httpserver"
-       "goshs.de/goshs/v2/logger"
        "github.com/pkg/sftp"
        gossh "golang.org/x/crypto/ssh"
+       "goshs.de/goshs/v2/httpserver"
+       "goshs.de/goshs/v2/logger"
 )
 
 var authorizedKeysMap map[string]bool
@@ -50,6 +50,7 @@
                sftpRoot = rewritePathWindows(sftpRoot)
        }
        clean := filepath.Clean("/" + strings.TrimLeft(clientPath, "/"))
+       clean = strings.TrimPrefix(clean, sftpRoot)
        abs := filepath.Join(sftpRoot, clean)
        rootClean := filepath.Clean(sftpRoot)
        if abs != rootClean && !strings.HasPrefix(abs, 
rootClean+string(filepath.Separator)) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/goshs-2.0.8/sftpserver/sftpserver.go 
new/goshs-2.0.9/sftpserver/sftpserver.go
--- old/goshs-2.0.8/sftpserver/sftpserver.go    2026-05-13 15:57:24.000000000 
+0200
+++ new/goshs-2.0.9/sftpserver/sftpserver.go    2026-05-27 15:49:51.000000000 
+0200
@@ -35,14 +35,14 @@
 func NewSFTPServer(opts *options.Options, wl *httpserver.Whitelist, webhook 
webhook.Webhook) *SFTPServer {
        return &SFTPServer{
                IP:          opts.IP,
-               Port:        opts.SFTPPort,
-               KeyFile:     opts.SFTPKeyFile,
+               Port:        opts.FTPPort,
+               KeyFile:     opts.FTPKeyFile,
                Username:    opts.Username,
                Password:    opts.Password,
                Root:        opts.Webroot,
                ReadOnly:    opts.ReadOnly,
                UploadOnly:  opts.UploadOnly,
-               HostKeyFile: opts.SFTPHostKeyFile,
+               HostKeyFile: opts.FTPHostKeyFile,
                Webhook:     webhook,
                Whitelist:   wl,
        }
@@ -164,5 +164,5 @@
                }
        }
 
-       logger.HandleWebhookSend(message, "sftp", s.Webhook)
+       logger.HandleWebhookSend(message, "ftp", s.Webhook)
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/goshs-2.0.8/utils/utils.go 
new/goshs-2.0.9/utils/utils.go
--- old/goshs-2.0.8/utils/utils.go      2026-05-13 15:57:24.000000000 +0200
+++ new/goshs-2.0.9/utils/utils.go      2026-05-27 15:49:51.000000000 +0200
@@ -98,7 +98,7 @@
        return string(bytes)
 }
 
-func RegisterZeroconfMDNS(ssl bool, webPort int, webdav bool, webdavPort int, 
sftp bool, sftpPort int, smtp bool, smtpPort int, dns bool, dnsPort int, smb 
bool, smbPort int, ldap bool, ldapPort int) error {
+func RegisterZeroconfMDNS(ssl bool, webPort int, webdav bool, webdavPort int, 
smtp bool, smtpPort int, dns bool, dnsPort int, smb bool, smbPort int, ldap 
bool, ldapPort int, ftp bool, ftpSFTPMode bool, ftpPort int) error {
        // Register zeroconf mDNS
        hostname, err := os.Hostname()
        if err != nil {
@@ -159,24 +159,6 @@
                logger.Infof("mDNS service registered as %s://%s.local:%d", 
out, hostname, webdavPort)
        }
 
-       // Register sftp if enabled
-       if sftp {
-               zeroSFTP, err := zeroconf.Register(
-                       "goshs SFTP",
-                       "_ssh._tcp",
-                       "local.",
-                       sftpPort,
-                       []string{fmt.Sprintf("host=%s.local", hostname), 
"subsystem=sftp", "path=/", fmt.Sprintf("version=%s", 
goshsversion.GoshsVersion)},
-                       nil,
-               )
-               if err != nil {
-                       return fmt.Errorf("zeroconf mDNS did not register 
successfully: %+v", err)
-               }
-               defer zeroSFTP.Shutdown()
-
-               logger.Infof("mDNS service registered as ssh://%s.local:%d", 
hostname, sftpPort)
-       }
-
        // Register smtp if enabled
        if smtp {
                zeroDav, err := zeroconf.Register(
@@ -249,5 +231,36 @@
                logger.Infof("mDNS service registered as ldap://%s.local:%d";, 
hostname, ldapPort)
        }
 
+       // Register ftp/sftp if enabled
+       if ftp {
+               var ftpServiceType, ftpProto, ftpName string
+               var ftpTxtRecords []string
+               if ftpSFTPMode {
+                       ftpServiceType = "_ssh._tcp"
+                       ftpProto = "ssh"
+                       ftpName = "goshs SFTP"
+                       ftpTxtRecords = []string{fmt.Sprintf("host=%s.local", 
hostname), "subsystem=sftp", "path=/", fmt.Sprintf("version=%s", 
goshsversion.GoshsVersion)}
+               } else {
+                       ftpServiceType = "_ftp._tcp"
+                       ftpProto = "ftp"
+                       ftpName = "goshs FTP"
+                       ftpTxtRecords = []string{fmt.Sprintf("host=%s.local", 
hostname), fmt.Sprintf("version=%s", goshsversion.GoshsVersion)}
+               }
+               zeroFTP, err := zeroconf.Register(
+                       ftpName,
+                       ftpServiceType,
+                       "local.",
+                       ftpPort,
+                       ftpTxtRecords,
+                       nil,
+               )
+               if err != nil {
+                       return fmt.Errorf("zeroconf mDNS did not register 
successfully: %+v", err)
+               }
+               defer zeroFTP.Shutdown()
+
+               logger.Infof("mDNS service registered as %s://%s.local:%d", 
ftpProto, hostname, ftpPort)
+       }
+
        return nil
 }

++++++ vendor.tar.gz ++++++
/work/SRC/openSUSE:Factory/goshs/vendor.tar.gz 
/work/SRC/openSUSE:Factory/.goshs.new.1937/vendor.tar.gz differ: char 15, line 1

Reply via email to