package main

import (
	"errors"
	"fmt"
	"io"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"
	"flag"
	"log"
	"goftp.io/server"
	"github.com/ProtonMail/gopenpgp/v3/crypto"
)

type ForwarderDriver struct {
	RecipientKey *crypto.Key
	OutputPath string
	server.Perm
}

type myFileInfo struct {
	mode  os.FileMode
	owner string
	group string
}

func (f *myFileInfo) Mode() os.FileMode {
	return os.ModeDir
}

func (f *myFileInfo) Owner() string {
	return ""
}

func (f *myFileInfo) Group() string {
	return ""
}

func (f *myFileInfo) Name() string {
	return ""
}

func (f *myFileInfo) Size() int64 {
	return 0
}

func (f *myFileInfo) Sys() any {
	return 0
}

func (f *myFileInfo) ModTime() time.Time {
	return time.Now()
}

func (f *myFileInfo) IsDir() bool {
	return true
}

func (driver *ForwarderDriver) Init(conn *server.Conn) {
	//driver.conn = conn
}

func (driver *ForwarderDriver) ChangeDir(path string) error {
	log.Printf("ChangeDir: %v", path)
	return nil
}

func (driver *ForwarderDriver) Stat(path string) (server.FileInfo, error) {
	log.Printf("Stat: %v", path)
	if (path == "/") {
		return &myFileInfo{}, nil
	} else {
		return nil, errors.New("Not Implemented")
	}
}

func (driver *ForwarderDriver) ListDir(path string, callback func(server.FileInfo) error) error {
	return errors.New("Not Implemented")
}

func (driver *ForwarderDriver) DeleteDir(path string) error {
	return errors.New("Not Implemented")
}

func (driver *ForwarderDriver) DeleteFile(path string) error {
	return errors.New("Not Implemented")
}

func (driver *ForwarderDriver) Rename(fromPath string, toPath string) error {
	return errors.New("Not Implemented")
}

func (driver *ForwarderDriver) MakeDir(path string) error {
	return errors.New("Not Implemented")
}

func (driver *ForwarderDriver) GetFile(path string, offset int64) (int64, io.ReadCloser, error) {
	return 0, nil, errors.New("Not Implemented")
}

func (driver *ForwarderDriver) PutFile(destPath string, data io.Reader, appendData bool) (int64, error) {
	log.Printf("Receiving file %v", destPath)

	dest_filename := path.Base(destPath)

	t := time.Now()
	encrypted_filename := fmt.Sprintf("scan_%04d-%02d-%02d-%02d-%02d_%v.asc", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), dest_filename)

	encrypted_file_path := filepath.Join(driver.OutputPath, encrypted_filename)

	log.Printf("Writing to %v", encrypted_file_path)

	log.Printf("Opening output file")
	encryptedFile, err := os.Create(encrypted_file_path)
	if err != nil {
		log.Printf("Error: %v", err)
		return 0, err
	}

	log.Printf("Preparing GPG encryption handle")
	pgp := crypto.PGP()
	encHandle, err := pgp.Encryption().Recipient(driver.RecipientKey).New()
	if err != nil {
		log.Printf("Error: %v", err)
		return 0, err
	}

	log.Printf("Preparing encrypting writer")
	encryptingWriter, err := encHandle.EncryptingWriter(encryptedFile, crypto.Armor)
	if err != nil {
		log.Printf("Error: %v", err)
		return 0, err
	}

	log.Printf("Read, encrypt and write data")
	transferred_bytes, err := io.Copy(encryptingWriter, data)
	if err != nil {
		log.Printf("Error: %v", err)
		return 0, err
	}

	encryptingWriter.Close()
	encryptedFile.Close()

	log.Printf("Transferred bytes: %v", transferred_bytes)
	return transferred_bytes, err
}

type ForwarderDriverFactory struct {
	RecipientKey *crypto.Key
	OutputPath string
	server.Perm
}

func (factory *ForwarderDriverFactory) NewDriver() (server.Driver, error) {
	return &ForwarderDriver{factory.RecipientKey, factory.OutputPath, factory.Perm}, nil
}


func main() {

	var (
		user = flag.String("user", "admin", "Username for login")
		pass = flag.String("pass", "123456", "Password for login")
		port = flag.Int("port", 2121, "Port")
		host = flag.String("host", "localhost", "Host")
		passiveports = flag.String("passiveports", "2130-2134", "Passive ports")
		gpgkey_path = flag.String("gpgkey", "", "File of the public GPG key to encrypt files to")
		output_path = flag.String("output", ".", "Path to directory to write encrypted files to")
	)
	flag.Parse()

	absolute_output_path, err := filepath.Abs(*output_path)
	if err != nil {
		log.Fatalf("Error: %v", err)
	}

	log.Printf("Writing encrypted files to %v", absolute_output_path)

	if *gpgkey_path == "" {
		log.Fatalf("Specify path to GPG key with -gpgkey")
	}

	gpgkey_file, err := os.Open(*gpgkey_path)
	if err != nil {
		log.Fatalf("Error: %v", err)
	}

	gpgkey, err := crypto.NewKeyFromReader(gpgkey_file)
	if err != nil {
		log.Fatalf("Error: %v", err)
	}

	gpgkey_file.Close()

	log.Printf("Encrypting files to %v", strings.ToUpper(gpgkey.GetFingerprint()))

	if !gpgkey.CanEncrypt(time.Now().Unix()) {
		log.Fatalf("Error: Provided GPG key does not support encryption")
	}

	factory := &ForwarderDriverFactory{
		RecipientKey: gpgkey,
		OutputPath: absolute_output_path,
		Perm: server.NewSimplePerm("user","group"),
	}

	opts := &server.ServerOpts{
		Factory:  factory,
		Port:     *port,
		Hostname: *host,
		Auth:     &server.SimpleAuth{Name: *user, Password: *pass},
		PassivePorts: *passiveports,
	}

	log.Printf("Starting ftp server on %v:%v", opts.Hostname, opts.Port)
	log.Printf("Username %v, Password %v", *user, *pass)
	server := server.NewServer(opts)

	err = server.ListenAndServe()
	if err != nil {
		log.Fatal("Error starting server:", err)
	}
}