package main import ( "errors" "fmt" "io" "os" "path/filepath" "strings" "time" ftpd "github.com/goftp/server" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/postgres" system "dev.sum7.eu/sum7/warehost/system" ) type FileDriver struct { RootPath string 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 "warehost" } func (f *FackFileInfo) Size() int64 { return 0 } func (f *FackFileInfo) ModTime() time.Time { return time.Now() } func (f *FackFileInfo) Group() string { return "http" } 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", 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 "data": root = fmt.Sprintf(driver.RootPath, driver.login.ID) paths = append([]string{paths[0]}, paths[2:]...) real = true } } 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 { if path == "/" { for _, i := range []string{"data", "web", "host"} { err := callback(&FackFileInfo{name: i}) 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 { RootPath string db *gorm.DB Perm ftpd.Perm } func (factory *FileDriverFactory) NewDriver() (ftpd.Driver, error) { return &FileDriver{RootPath: factory.RootPath, db: factory.db, Perm: factory.Perm}, nil }