2025-07-27 14:26:30 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"canguidev/shelfy/internal/db"
|
|
|
|
|
"canguidev/shelfy/internal/routes"
|
|
|
|
|
"canguidev/shelfy/internal/utils"
|
|
|
|
|
"crypto/tls"
|
|
|
|
|
"errors"
|
|
|
|
|
"log"
|
|
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"strings"
|
|
|
|
|
"time"
|
|
|
|
|
|
2025-07-27 18:06:11 +00:00
|
|
|
ftpserver "github.com/fclairamb/ftpserverlib"
|
2025-07-27 14:26:30 +00:00
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
|
"github.com/spf13/afero"
|
|
|
|
|
)
|
|
|
|
|
|
2025-07-27 18:06:11 +00:00
|
|
|
// ---------- FTP SERVERLIB DRIVERS ----------
|
2025-07-27 14:26:30 +00:00
|
|
|
|
2025-07-27 18:06:11 +00:00
|
|
|
type mainDriver struct{}
|
2025-07-27 14:26:30 +00:00
|
|
|
|
2025-07-27 18:06:11 +00:00
|
|
|
type clientDriver struct {
|
2025-07-27 14:26:30 +00:00
|
|
|
fs afero.Fs
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-27 18:06:11 +00:00
|
|
|
func (d *mainDriver) GetSettings() (*ftpserver.Settings, error) {
|
|
|
|
|
return &ftpserver.Settings{
|
|
|
|
|
ListenAddr: ":2121",
|
2025-07-27 19:14:44 +00:00
|
|
|
PublicHost: "163.172.68.103",
|
2025-07-27 18:06:11 +00:00
|
|
|
PassiveTransferPortRange: &ftpserver.PortRange{
|
|
|
|
|
Start: 30000,
|
|
|
|
|
End: 30100,
|
|
|
|
|
},
|
|
|
|
|
Banner: "Bienvenue sur le FTP Go!",
|
2025-07-27 14:26:30 +00:00
|
|
|
IdleTimeout: 300,
|
|
|
|
|
ConnectionTimeout: 60,
|
2025-07-27 19:14:44 +00:00
|
|
|
|
2025-07-27 14:26:30 +00:00
|
|
|
}, nil
|
|
|
|
|
}
|
2025-07-27 18:06:11 +00:00
|
|
|
|
|
|
|
|
func (d *mainDriver) ClientConnected(cc ftpserver.ClientContext) (string, error) {
|
|
|
|
|
log.Printf("[FTP] Nouvelle connexion depuis %s", cc.RemoteAddr())
|
|
|
|
|
return "Bienvenue FTP !", nil
|
|
|
|
|
}
|
|
|
|
|
func (d *mainDriver) ClientDisconnected(cc ftpserver.ClientContext) {
|
|
|
|
|
log.Printf("[FTP] Déconnexion client %s", cc.RemoteAddr())
|
2025-07-27 14:26:30 +00:00
|
|
|
}
|
2025-07-27 18:06:11 +00:00
|
|
|
func (d *mainDriver) GetTLSConfig() (*tls.Config, error) {
|
|
|
|
|
return nil, nil // Pas de TLS, à gérer si tu veux FTPS
|
|
|
|
|
}
|
2025-07-27 19:44:19 +00:00
|
|
|
import (
|
|
|
|
|
"errors"
|
|
|
|
|
"net"
|
|
|
|
|
// ... autres imports
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func (d *ftpMainDriver) AuthUser(cc ftpserverlib.ClientContext, user, pass string) (ftpserverlib.ClientDriver, error) {
|
|
|
|
|
remoteAddr := cc.RemoteAddr().String()
|
|
|
|
|
host, _, _ := net.SplitHostPort(remoteAddr)
|
|
|
|
|
log.Printf("[FTP] Tentative login user='%s' pass='%s' depuis %s", user, pass, host)
|
|
|
|
|
|
|
|
|
|
// 1. Login classique
|
2025-07-27 19:36:16 +00:00
|
|
|
if user == "cangui2089" && pass == "GHT30k7!" {
|
2025-07-27 19:44:19 +00:00
|
|
|
base := filepath.Clean("upload")
|
|
|
|
|
fs := afero.NewBasePathFs(afero.NewOsFs(), base)
|
|
|
|
|
log.Printf("[FTP] OK user %s", user)
|
|
|
|
|
return &ftpClientDriver{fs: fs}, nil
|
2025-07-27 17:06:27 +00:00
|
|
|
}
|
2025-07-27 19:44:19 +00:00
|
|
|
|
|
|
|
|
// 2. Autoriser anonymous depuis 192.168.1.123 uniquement
|
|
|
|
|
if user == "anonymous" && host == "82.65.73.115" {
|
2025-07-27 19:41:53 +00:00
|
|
|
base := filepath.Clean("upload")
|
|
|
|
|
fs := afero.NewBasePathFs(afero.NewOsFs(), base)
|
|
|
|
|
log.Printf("[FTP] Login ANONYMOUS autorisé pour %s", host)
|
|
|
|
|
return &ftpClientDriver{fs: fs}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3. Sinon refuse tout anonyme ou mauvais login
|
|
|
|
|
if user == "" || user == "anonymous" {
|
|
|
|
|
log.Printf("[FTP] Login anonymous refusé pour %s", host)
|
2025-07-27 19:44:19 +00:00
|
|
|
return nil, errors.New("anonymous login not allowed")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log.Printf("[FTP] Refus user=%s pass=%s", user, pass)
|
|
|
|
|
return nil, errors.New("invalid login")
|
2025-07-27 17:06:27 +00:00
|
|
|
}
|
2025-07-27 14:26:30 +00:00
|
|
|
|
2025-07-27 19:44:19 +00:00
|
|
|
|
2025-07-27 18:06:11 +00:00
|
|
|
// ---------- clientDriver = wrapper sur afero.Fs ----------
|
|
|
|
|
|
|
|
|
|
func (c *clientDriver) Name() string { return "aferofs" }
|
|
|
|
|
func (c *clientDriver) Create(name string) (afero.File, error) { return c.fs.Create(name) }
|
|
|
|
|
func (c *clientDriver) Mkdir(name string, perm os.FileMode) error { return c.fs.Mkdir(name, perm) }
|
|
|
|
|
func (c *clientDriver) MkdirAll(path string, perm os.FileMode) error { return c.fs.MkdirAll(path, perm) }
|
|
|
|
|
func (c *clientDriver) Open(name string) (afero.File, error) { return c.fs.Open(name) }
|
|
|
|
|
func (c *clientDriver) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) {
|
2025-07-27 19:14:44 +00:00
|
|
|
log.Printf("[FTP][DEBUG] Open: %s", name)
|
2025-07-27 14:26:30 +00:00
|
|
|
return c.fs.OpenFile(name, flag, perm)
|
|
|
|
|
}
|
2025-07-27 18:06:11 +00:00
|
|
|
func (c *clientDriver) Remove(name string) error { return c.fs.Remove(name) }
|
|
|
|
|
func (c *clientDriver) RemoveAll(path string) error { return c.fs.RemoveAll(path) }
|
|
|
|
|
func (c *clientDriver) Rename(old, new string) error { return c.fs.Rename(old, new) }
|
|
|
|
|
func (c *clientDriver) Stat(name string) (os.FileInfo, error) {
|
2025-07-27 19:14:44 +00:00
|
|
|
log.Printf("[FTP][DEBUG] Stat: %s", name)
|
2025-07-27 14:26:30 +00:00
|
|
|
return c.fs.Stat(name)
|
|
|
|
|
}
|
2025-07-27 18:06:11 +00:00
|
|
|
func (c *clientDriver) LstatIfPossible(name string) (os.FileInfo, bool, error) {
|
2025-07-27 14:26:30 +00:00
|
|
|
fi, err := c.fs.Stat(name)
|
|
|
|
|
return fi, false, err
|
|
|
|
|
}
|
2025-07-27 18:06:11 +00:00
|
|
|
func (c *clientDriver) Chmod(name string, mode os.FileMode) error {
|
2025-07-27 14:26:30 +00:00
|
|
|
fullPath := filepath.Join("upload", name)
|
|
|
|
|
return os.Chmod(fullPath, mode)
|
|
|
|
|
}
|
2025-07-27 18:06:11 +00:00
|
|
|
func (c *clientDriver) Chown(name string, uid, gid int) error {
|
|
|
|
|
fullPath := filepath.Join("upload", name)
|
|
|
|
|
return os.Chown(fullPath, uid, gid)
|
2025-07-27 14:26:30 +00:00
|
|
|
}
|
2025-07-27 18:06:11 +00:00
|
|
|
func (c *clientDriver) Chtimes(name string, atime, mtime time.Time) error {
|
2025-07-27 14:26:30 +00:00
|
|
|
fullPath := filepath.Join("upload", name)
|
|
|
|
|
return os.Chtimes(fullPath, atime, mtime)
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-27 18:06:11 +00:00
|
|
|
|
|
|
|
|
// ---------- MAIN ----------
|
2025-07-27 14:26:30 +00:00
|
|
|
|
|
|
|
|
func main() {
|
2025-07-27 18:06:11 +00:00
|
|
|
// Serveur FTP dans une goroutine
|
2025-07-27 14:26:30 +00:00
|
|
|
go func() {
|
2025-07-27 18:06:11 +00:00
|
|
|
ftp := ftpserver.NewFtpServer(&mainDriver{})
|
|
|
|
|
log.Println("[FTP] Serveur FTP en écoute sur ftp://cangui2089:GHT30k7!@localhost:2121 (upload/)")
|
|
|
|
|
if err := ftp.ListenAndServe(); err != nil {
|
|
|
|
|
log.Fatal("[FTP] Erreur:", err)
|
2025-07-27 14:26:30 +00:00
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
2025-07-27 18:06:11 +00:00
|
|
|
// Serveur HTTP Gin (classique)
|
2025-07-27 14:26:30 +00:00
|
|
|
bd := db.InitDB()
|
|
|
|
|
app := gin.Default()
|
|
|
|
|
|
|
|
|
|
api := app.Group("/api/v1")
|
|
|
|
|
routes.AddRoutes(api, bd)
|
|
|
|
|
utils.CreateDefaultFolder(bd)
|
|
|
|
|
|
|
|
|
|
app.Static("/static", "./web")
|
|
|
|
|
app.NoRoute(func(c *gin.Context) {
|
|
|
|
|
if strings.HasPrefix(c.Request.URL.Path, "/api/") {
|
|
|
|
|
c.JSON(404, gin.H{"error": "Not found"})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
c.File("./web/index.html")
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
log.Println("[HTTP] Serveur Gin sur http://localhost:8080")
|
2025-07-27 14:33:19 +00:00
|
|
|
app.Run(":8080")
|
2025-07-27 14:26:30 +00:00
|
|
|
}
|