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