186 خطوط
4.4 KiB
Go
186 خطوط
4.4 KiB
Go
package ftp
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"sync"
|
|
"time"
|
|
|
|
"ftp-server/config"
|
|
"ftp-server/database"
|
|
|
|
ftpserver "github.com/fclairamb/ftpserverlib"
|
|
"github.com/spf13/afero"
|
|
)
|
|
|
|
// Server FTP服务器
|
|
type Server struct {
|
|
config *config.Config
|
|
db *database.DB
|
|
ftpServer *ftpserver.FtpServer
|
|
onlineMu sync.RWMutex
|
|
onlineUsers map[string]*database.OnlineUser
|
|
}
|
|
|
|
// NewServer 创建FTP服务器
|
|
func NewServer(cfg *config.Config, db *database.DB) *Server {
|
|
return &Server{
|
|
config: cfg,
|
|
db: db,
|
|
onlineUsers: make(map[string]*database.OnlineUser),
|
|
}
|
|
}
|
|
|
|
// Start 启动FTP服务器
|
|
func (s *Server) Start() error {
|
|
ftpCfg := s.config.Get().FTP
|
|
|
|
// 确保FTP根目录存在
|
|
if err := os.MkdirAll(ftpCfg.RootDir, 0755); err != nil {
|
|
return fmt.Errorf("创建FTP根目录失败: %w", err)
|
|
}
|
|
|
|
server := ftpserver.NewFtpServer(s)
|
|
s.ftpServer = server
|
|
|
|
go func() {
|
|
if err := server.ListenAndServe(); err != nil {
|
|
log.Printf("FTP服务器错误: %v", err)
|
|
}
|
|
}()
|
|
|
|
log.Printf("FTP服务器已启动: %s:%d", ftpCfg.Host, ftpCfg.Port)
|
|
return nil
|
|
}
|
|
|
|
// Stop 停止FTP服务器
|
|
func (s *Server) Stop() {
|
|
if s.ftpServer != nil {
|
|
s.ftpServer.Stop()
|
|
log.Println("FTP服务器已停止")
|
|
}
|
|
}
|
|
|
|
// GetOnlineUsers 获取在线用户列表
|
|
func (s *Server) GetOnlineUsers() []database.OnlineUser {
|
|
s.onlineMu.RLock()
|
|
defer s.onlineMu.RUnlock()
|
|
|
|
result := make([]database.OnlineUser, 0, len(s.onlineUsers))
|
|
for _, u := range s.onlineUsers {
|
|
result = append(result, *u)
|
|
}
|
|
return result
|
|
}
|
|
|
|
// --- 实现 ftpserverlib.MainDriver 接口 ---
|
|
|
|
// GetSettings 返回FTP服务器设置
|
|
func (s *Server) GetSettings() (*ftpserver.Settings, error) {
|
|
ftpCfg := s.config.Get().FTP
|
|
return &ftpserver.Settings{
|
|
ListenAddr: fmt.Sprintf("%s:%d", ftpCfg.Host, ftpCfg.Port),
|
|
PassiveTransferPortRange: ftpserver.PortRange{
|
|
Start: ftpCfg.PassivePortMin,
|
|
End: ftpCfg.PassivePortMax,
|
|
},
|
|
ConnectionTimeout: int(time.Duration(ftpCfg.IdleTimeout) * time.Second),
|
|
}, nil
|
|
}
|
|
|
|
// ClientConnected 客户端连接
|
|
func (s *Server) ClientConnected(cc ftpserver.ClientContext) (string, error) {
|
|
return "220 Welcome to FTP Server\r\n", nil
|
|
}
|
|
|
|
// ClientDisconnected 客户端断开
|
|
func (s *Server) ClientDisconnected(cc ftpserver.ClientContext) {
|
|
s.onlineMu.Lock()
|
|
defer s.onlineMu.Unlock()
|
|
|
|
for id, u := range s.onlineUsers {
|
|
if u.IP == cc.RemoteAddr().String() {
|
|
delete(s.onlineUsers, id)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// AuthUser 认证用户
|
|
func (s *Server) AuthUser(cc ftpserver.ClientContext, username, password string) (ftpserver.ClientDriver, error) {
|
|
ftpCfg := s.config.Get().FTP
|
|
|
|
// 匿名登录
|
|
if username == "anonymous" {
|
|
if !ftpCfg.EnableAnonymous {
|
|
return nil, fmt.Errorf("匿名访问未启用")
|
|
}
|
|
if err := os.MkdirAll(ftpCfg.RootDir, 0755); err != nil {
|
|
return nil, fmt.Errorf("创建根目录失败")
|
|
}
|
|
osFs := afero.NewOsFs()
|
|
boundedFs := afero.NewBasePathFs(osFs, ftpCfg.RootDir)
|
|
return boundedFs, nil
|
|
}
|
|
|
|
// 数据库用户认证
|
|
user, err := s.db.GetUser(username)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("认证失败")
|
|
}
|
|
if user == nil || !user.Enabled {
|
|
return nil, fmt.Errorf("用户不存在或已禁用")
|
|
}
|
|
if user.Password != password {
|
|
s.db.AddLog(&database.FTPLog{
|
|
Username: username,
|
|
IP: cc.RemoteAddr().String(),
|
|
Action: "login_failed",
|
|
Status: "failed",
|
|
})
|
|
return nil, fmt.Errorf("密码错误")
|
|
}
|
|
|
|
// 记录登录日志
|
|
s.db.AddLog(&database.FTPLog{
|
|
Username: username,
|
|
IP: cc.RemoteAddr().String(),
|
|
Action: "login",
|
|
Status: "success",
|
|
})
|
|
|
|
// 记录在线用户
|
|
s.onlineMu.Lock()
|
|
s.onlineUsers[username+"_"+cc.RemoteAddr().String()] = &database.OnlineUser{
|
|
Username: username,
|
|
IP: cc.RemoteAddr().String(),
|
|
LoginTime: time.Now(),
|
|
LastActivity: time.Now(),
|
|
CurrentDir: user.HomeDir,
|
|
}
|
|
s.onlineMu.Unlock()
|
|
|
|
// 确保用户目录存在(自动创建)
|
|
if err := os.MkdirAll(user.HomeDir, 0755); err != nil {
|
|
return nil, fmt.Errorf("创建用户目录失败: %v", err)
|
|
}
|
|
|
|
// 返回 afero.Fs 作为 ClientDriver
|
|
osFs := afero.NewOsFs()
|
|
boundedFs := afero.NewBasePathFs(osFs, user.HomeDir)
|
|
|
|
// 根据权限设置只读
|
|
if user.Permissions == "read" {
|
|
return afero.NewReadOnlyFs(boundedFs), nil
|
|
}
|
|
|
|
return boundedFs, nil
|
|
}
|
|
|
|
// GetTLSConfig 获取TLS配置
|
|
func (s *Server) GetTLSConfig() (*tls.Config, error) {
|
|
return nil, fmt.Errorf("TLS未配置")
|
|
}
|