package main import ( "errors" "fmt" "io" "os" "path/filepath" "strconv" "strings" "time" ftpd "github.com/goftp/server" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/postgres" host "dev.sum7.eu/sum7/warehost/modul/host" web "dev.sum7.eu/sum7/warehost/modul/web" system "dev.sum7.eu/sum7/warehost/system" ) const ( DriverOwner = "warehost" DriverGroup = "http" DriverFolderOwn = "home" DriverFolderDomain = "domain" DriverFolderWeb = "web" ) type FileDriver struct { config *Config db *gorm.DB Perm ftpd.Perm login system.Login conn *ftpd.Conn } type FackFileInfo struct { os.FileInfo name string } func (f *FackFileInfo) Mode() os.FileMode { return os.ModeDir } func (f *FackFileInfo) IsDir() bool { return true } func (f *FackFileInfo) Name() string { return f.name } func (f *FackFileInfo) Owner() string { return DriverOwner } func (f *FackFileInfo) Size() int64 { return 0 } func (f *FackFileInfo) ModTime() time.Time { return time.Now() } func (f *FackFileInfo) Group() string { return DriverGroup } type FileInfo struct { os.FileInfo mode os.FileMode owner string group string } func (f *FileInfo) Mode() os.FileMode { return f.mode } func (f *FileInfo) Owner() string { return f.owner } func (f *FileInfo) Group() string { return f.group } func (driver *FileDriver) chechLogin() { if driver.login.ID <= 0 && driver.conn.IsLogin() { driver.db.Where("mail = ?", driver.conn.LoginUser()).First(&driver.login) fmt.Printf("Connection:%s:%d\n", driver.conn.LoginUser(), driver.login.ID) } } func (driver *FileDriver) realPath(path string) (string, bool) { driver.chechLogin() paths := strings.Split(path, "/") root := "/dev/null" real := false if len(paths) > 1 && driver.login.ID > 0 { switch paths[1] { case DriverFolderOwn: root = fmt.Sprintf(driver.config.Own, driver.login.ID) paths = paths[2:] real = true break case DriverFolderDomain: if len(paths) > 2 { var web host.Web domain := paths[2] if driver.login.Superadmin { driver.db.Joins("LEFT JOIN host_domain ON host_domain.id=host_web.domain").Where("CONCAT(host_web.subdomain,'.',host_domain.fqdn) = ?", domain).Or("host_web.subdomain='' AND host_domain.fqdn = ?", domain).First(&web) } else { driver.db.Joins("LEFT JOIN host_domain ON host_domain.id=host_web.domain").Joins("LEFT JOIN host_web_ftpaccess ftp ON ftp.web=host_web.id").Where("ftp.login = ?", driver.login.ID).Where("CONCAT(host_web.subdomain,'.',host_domain.fqdn) = ?", domain).Or("host_web.subdomain='' AND host_domain.fqdn = ?", domain).First(&web) } if web.ID > 0 { root = fmt.Sprintf(driver.config.Host, domain) paths = paths[3:] real = true } } break case DriverFolderWeb: if len(paths) > 2 { id, _ := strconv.ParseInt(paths[2], 10, 64) res := driver.db.Where(map[string]int64{"website": id, "login": driver.login.ID}).Find(&web.Manager{}) if !res.RecordNotFound() || driver.login.Superadmin { root = fmt.Sprintf(driver.config.Web, id) paths = paths[3:] real = true } } break } } return filepath.Join(append([]string{root}, paths...)...), real } func (driver *FileDriver) Init(conn *ftpd.Conn) { driver.conn = conn } func (driver *FileDriver) ChangeDir(path string) error { rPath, real := driver.realPath(path) if !real { return nil } f, err := os.Lstat(rPath) if err != nil { return err } if f.IsDir() { return nil } return errors.New("Not a directory") } func (driver *FileDriver) Stat(path string) (ftpd.FileInfo, error) { basepath, real := driver.realPath(path) var f os.FileInfo if real { rPath, err := filepath.Abs(basepath) if err != nil { return nil, err } f, err = os.Lstat(rPath) if err != nil { return nil, err } } else { f = &FackFileInfo{name: path} } mode, err := driver.Perm.GetMode(path) if err != nil { return nil, err } if f.IsDir() { mode |= os.ModeDir } owner, err := driver.Perm.GetOwner(path) if err != nil { return nil, err } group, err := driver.Perm.GetGroup(path) if err != nil { return nil, err } return &FileInfo{f, mode, owner, group}, nil } func (driver *FileDriver) ListDir(path string, callback func(ftpd.FileInfo) error) error { basepath, real := driver.realPath(path) if real { filepath.Walk(basepath, func(f string, info os.FileInfo, err error) error { rPath, _ := filepath.Rel(basepath, f) if rPath == info.Name() { mode, err := driver.Perm.GetMode(rPath) if err != nil { return err } if info.IsDir() { mode |= os.ModeDir } owner, err := driver.Perm.GetOwner(rPath) if err != nil { return err } group, err := driver.Perm.GetGroup(rPath) if err != nil { return err } err = callback(&FileInfo{info, mode, owner, group}) if err != nil { return err } } return nil }) return nil } else { switch path { case "/": for _, i := range []string{DriverFolderOwn, DriverFolderDomain, DriverFolderWeb} { err := callback(&FackFileInfo{name: i}) if err != nil { return err } } return nil case fmt.Sprintf("/%s", DriverFolderDomain): var list []*host.Web if driver.login.Superadmin { driver.db.Preload("Domain").Find(&list) } else { driver.db.Joins("LEFT JOIN host_web_ftpaccess ftp ON ftp.web=host_web.id").Where("ftp.login = ?", driver.login.ID).Preload("Domain").Find(&list) } for _, i := range list { domain := i.Domain.FQDN if len(i.Subdomain) > 0 { domain = fmt.Sprintf("%s.%s", i.Subdomain, domain) } err := callback(&FackFileInfo{name: domain}) if err != nil { return err } } return nil case fmt.Sprintf("/%s", DriverFolderWeb): var list []*web.Manager if driver.login.Superadmin { driver.db.Find(&list) } else { driver.db.Where("login = ?", driver.login.ID).Find(&list) } for _, i := range list { err := callback(&FackFileInfo{name: fmt.Sprintf("%d", i.WebsiteID)}) if err != nil { return err } } return nil } return errors.New("No path") } } func (driver *FileDriver) DeleteDir(path string) error { rPath, real := driver.realPath(path) if !real { return errors.New("Warehost folders are not deletable") } f, err := os.Lstat(rPath) if err != nil { return err } if f.IsDir() { return os.Remove(rPath) } return errors.New("Not a directory") } func (driver *FileDriver) DeleteFile(path string) error { rPath, real := driver.realPath(path) if !real { return errors.New("Warehost files are not deletable") } f, err := os.Lstat(rPath) if err != nil { return err } if !f.IsDir() { return os.Remove(rPath) } return errors.New("Not a file") } func (driver *FileDriver) Rename(fromPath string, toPath string) error { oldPath, realOld := driver.realPath(fromPath) newPath, realNew := driver.realPath(toPath) if !realOld || !realNew { return errors.New("Warehost files/folders are not moveable") } return os.Rename(oldPath, newPath) } func (driver *FileDriver) MakeDir(path string) error { rPath, real := driver.realPath(path) if !real { return errors.New("Warehost folders are not createable") } return os.Mkdir(rPath, os.ModePerm) } func (driver *FileDriver) GetFile(path string, offset int64) (int64, io.ReadCloser, error) { rPath, real := driver.realPath(path) if !real { return 0, nil, errors.New("Warehost files are not downloadable") } f, err := os.Open(rPath) if err != nil { return 0, nil, err } info, err := f.Stat() if err != nil { return 0, nil, err } f.Seek(offset, os.SEEK_SET) return info.Size(), f, nil } func (driver *FileDriver) PutFile(destPath string, data io.Reader, appendData bool) (int64, error) { rPath, real := driver.realPath(destPath) if !real { return 0, errors.New("Warehost files are not replaceable") } var isExist bool f, err := os.Lstat(rPath) if err == nil { isExist = true if f.IsDir() { return 0, errors.New("A dir has the same name") } } else { if os.IsNotExist(err) { isExist = false } else { return 0, errors.New(fmt.Sprintln("Put File error:", err)) } } if appendData && !isExist { appendData = false } if !appendData { if isExist { err = os.Remove(rPath) if err != nil { return 0, err } } f, err := os.Create(rPath) if err != nil { return 0, err } defer f.Close() bytes, err := io.Copy(f, data) if err != nil { return 0, err } return bytes, nil } of, err := os.OpenFile(rPath, os.O_APPEND|os.O_RDWR, 0660) if err != nil { return 0, err } defer of.Close() _, err = of.Seek(0, os.SEEK_END) if err != nil { return 0, err } bytes, err := io.Copy(of, data) if err != nil { return 0, err } return bytes, nil } type FileDriverFactory struct { config *Config db *gorm.DB Perm ftpd.Perm } func (factory *FileDriverFactory) NewDriver() (ftpd.Driver, error) { return &FileDriver{config: factory.config, db: factory.db, Perm: factory.Perm}, nil }