1
0
Files
FTP-Server/ftp/server.go
T

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未配置")
}