2016-11-27 10:46:50 +01:00
package main
import (
"errors"
"fmt"
"io"
"os"
"path/filepath"
2016-12-20 00:35:03 +01:00
"strconv"
2016-11-27 10:46:50 +01:00
"strings"
"time"
ftpd "github.com/goftp/server"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/postgres"
2016-12-19 12:24:18 +01:00
host "dev.sum7.eu/sum7/warehost/modul/host"
web "dev.sum7.eu/sum7/warehost/modul/web"
2016-11-27 10:46:50 +01:00
system "dev.sum7.eu/sum7/warehost/system"
)
2016-12-19 12:24:18 +01:00
const (
DriverOwner = "warehost"
DriverGroup = "http"
DriverFolderOwn = "home"
DriverFolderDomain = "domain"
DriverFolderWeb = "web"
)
2016-11-27 10:46:50 +01:00
type FileDriver struct {
2016-12-19 12:52:43 +01:00
config * Config
2016-12-19 12:24:18 +01:00
db * gorm . DB
Perm ftpd . Perm
login system . Login
conn * ftpd . Conn
2016-11-27 10:46:50 +01:00
}
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 {
2016-12-19 12:24:18 +01:00
return DriverOwner
2016-11-27 10:46:50 +01:00
}
func ( f * FackFileInfo ) Size ( ) int64 {
return 0
}
func ( f * FackFileInfo ) ModTime ( ) time . Time {
return time . Now ( )
}
func ( f * FackFileInfo ) Group ( ) string {
2016-12-19 12:24:18 +01:00
return DriverGroup
2016-11-27 10:46:50 +01:00
}
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 )
2016-12-20 00:35:03 +01:00
fmt . Printf ( "Connection:%s:%d\n" , driver . conn . LoginUser ( ) , driver . login . ID )
2016-11-27 10:46:50 +01:00
}
}
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 ] {
2016-12-19 12:24:18 +01:00
case DriverFolderOwn :
2016-12-19 12:52:43 +01:00
root = fmt . Sprintf ( driver . config . Own , driver . login . ID )
2016-12-20 00:35:03 +01:00
paths = paths [ 2 : ]
2016-11-27 10:46:50 +01:00
real = true
2016-12-20 00:35:03 +01:00
break
2016-12-19 12:24:18 +01:00
case DriverFolderDomain :
if len ( paths ) > 2 {
2016-12-20 00:35:03 +01:00
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 AND 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
}
2016-12-19 12:24:18 +01:00
}
2016-12-20 00:35:03 +01:00
break
2016-12-19 12:24:18 +01:00
case DriverFolderWeb :
if len ( paths ) > 2 {
2016-12-20 00:35:03 +01:00
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
}
2016-12-19 12:24:18 +01:00
}
2016-12-20 00:35:03 +01:00
break
2016-11-27 10:46:50 +01:00
}
}
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 {
2016-12-19 12:24:18 +01:00
switch path {
case "/" :
for _ , i := range [ ] string { DriverFolderOwn , DriverFolderDomain , DriverFolderWeb } {
2016-11-27 10:46:50 +01:00
err := callback ( & FackFileInfo { name : i } )
if err != nil {
return err
}
}
return nil
2016-12-19 12:24:18 +01:00
case fmt . Sprintf ( "/%s" , DriverFolderDomain ) :
var list [ ] * host . Web
2016-12-20 00:35:03 +01:00
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 AND ftp.login = ?" , driver . login . ID ) . Preload ( "Domain" ) . Find ( & list )
}
2016-12-19 12:24:18 +01:00
for _ , i := range list {
domain := i . Domain . FQDN
2016-12-19 12:52:43 +01:00
if len ( i . Subdomain ) > 0 {
2016-12-19 12:24:18 +01:00
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 ) :
2016-12-20 00:35:03 +01:00
var list [ ] * web . Manager
if driver . login . Superadmin {
driver . db . Find ( & list )
} else {
driver . db . Where ( "login = ?" , driver . login . ID ) . Find ( & list )
}
2016-12-19 12:24:18 +01:00
for _ , i := range list {
2016-12-20 00:35:03 +01:00
err := callback ( & FackFileInfo { name : fmt . Sprintf ( "%d" , i . WebsiteID ) } )
2016-12-19 12:24:18 +01:00
if err != nil {
return err
}
}
return nil
2016-11-27 10:46:50 +01:00
}
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 {
2016-12-19 12:52:43 +01:00
config * Config
2016-12-19 12:24:18 +01:00
db * gorm . DB
Perm ftpd . Perm
2016-11-27 10:46:50 +01:00
}
func ( factory * FileDriverFactory ) NewDriver ( ) ( ftpd . Driver , error ) {
2016-12-19 12:24:18 +01:00
return & FileDriver { config : factory . config , db : factory . db , Perm : factory . Perm } , nil
2016-11-27 10:46:50 +01:00
}