0.2.8
This commit is contained in:
266
cmd/client-ftp/main.go
Normal file
266
cmd/client-ftp/main.go
Normal file
@@ -0,0 +1,266 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/Sudo-Ivan/reticulum-go/internal/config"
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/common"
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/destination"
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/identity"
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/link"
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/packet"
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/resource"
|
||||
"github.com/Sudo-Ivan/reticulum-go/pkg/transport"
|
||||
)
|
||||
|
||||
const (
|
||||
APP_NAME = "example_utilities"
|
||||
APP_ASPECT = "filetransfer"
|
||||
)
|
||||
|
||||
var (
|
||||
configPath = flag.String("config", "", "Path to config file")
|
||||
servePath = flag.String("serve", "", "Directory to serve files from")
|
||||
)
|
||||
|
||||
type FileServer struct {
|
||||
config *common.ReticulumConfig
|
||||
transport *transport.Transport
|
||||
interfaces []common.NetworkInterface
|
||||
identity *identity.Identity
|
||||
servePath string
|
||||
}
|
||||
|
||||
func NewFileServer(cfg *common.ReticulumConfig, servePath string) (*FileServer, error) {
|
||||
if cfg == nil {
|
||||
var err error
|
||||
cfg, err = config.InitConfig()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize config: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
t, err := transport.NewTransport(cfg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize transport: %v", err)
|
||||
}
|
||||
|
||||
id, err := identity.New()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create identity: %v", err)
|
||||
}
|
||||
|
||||
return &FileServer{
|
||||
config: cfg,
|
||||
transport: t,
|
||||
interfaces: make([]common.NetworkInterface, 0),
|
||||
identity: id,
|
||||
servePath: servePath,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *FileServer) OnLinkEstablished(l *link.Link) {
|
||||
s.handleLinkEstablished(l)
|
||||
}
|
||||
|
||||
func (s *FileServer) Start() error {
|
||||
dest, err := destination.New(
|
||||
s.identity,
|
||||
destination.OUT,
|
||||
destination.SINGLE,
|
||||
APP_NAME,
|
||||
APP_ASPECT,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create destination: %v", err)
|
||||
}
|
||||
|
||||
callback := func(l interface{}) {
|
||||
if link, ok := l.(*link.Link); ok {
|
||||
s.OnLinkEstablished(link)
|
||||
}
|
||||
}
|
||||
|
||||
dest.SetLinkEstablishedCallback(callback)
|
||||
|
||||
log.Printf("File server started. Server hash: %s", s.identity.Hex())
|
||||
log.Printf("Serving directory: %s", s.servePath)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *FileServer) handleLinkEstablished(l *link.Link) {
|
||||
log.Printf("Client connected")
|
||||
|
||||
l.SetPacketCallback(func(data []byte, p *packet.Packet) {
|
||||
s.handlePacket(data, l)
|
||||
})
|
||||
|
||||
l.SetResourceCallback(func(r interface{}) bool {
|
||||
if res, ok := r.(*resource.Resource); ok {
|
||||
return s.handleResource(res)
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
func (s *FileServer) handlePacket(data []byte, l *link.Link) {
|
||||
if string(data) == "LIST" {
|
||||
files, err := s.getFileList()
|
||||
if err != nil {
|
||||
log.Printf("Error getting file list: %v", err)
|
||||
l.Teardown()
|
||||
return
|
||||
}
|
||||
|
||||
if err := l.SendPacket(files); err != nil {
|
||||
log.Printf("Error sending file list: %v", err)
|
||||
l.Teardown()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *FileServer) handleResource(r *resource.Resource) bool {
|
||||
filename := filepath.Join(s.servePath, r.GetName())
|
||||
file, err := os.Create(filename)
|
||||
if err != nil {
|
||||
log.Printf("Failed to create file: %v", err)
|
||||
return false
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
written, err := io.Copy(file, r)
|
||||
if err != nil {
|
||||
log.Printf("Failed to write file: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
log.Printf("Received file: %s (%d bytes)", filename, written)
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *FileServer) getFileList() ([]byte, error) {
|
||||
files, err := os.ReadDir(s.servePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var fileList []string
|
||||
for _, file := range files {
|
||||
if !file.IsDir() {
|
||||
fileList = append(fileList, file.Name())
|
||||
}
|
||||
}
|
||||
|
||||
return []byte(fmt.Sprintf("%v", fileList)), nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
if *servePath == "" {
|
||||
log.Fatal("Please specify a directory to serve with -serve")
|
||||
}
|
||||
|
||||
var cfg *common.ReticulumConfig
|
||||
var err error
|
||||
|
||||
if *configPath == "" {
|
||||
cfg, err = config.InitConfig()
|
||||
} else {
|
||||
cfg, err = config.LoadConfig(*configPath)
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to load config: %v", err)
|
||||
}
|
||||
|
||||
server, err := NewFileServer(cfg, *servePath)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create server: %v", err)
|
||||
}
|
||||
|
||||
if err := server.Start(); err != nil {
|
||||
log.Fatalf("Failed to start server: %v", err)
|
||||
}
|
||||
|
||||
// Start watching the directory for changes
|
||||
go server.watchDirectory()
|
||||
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-sigChan
|
||||
}
|
||||
|
||||
func (s *FileServer) watchDirectory() {
|
||||
for {
|
||||
time.Sleep(1 * time.Second)
|
||||
files, err := os.ReadDir(s.servePath)
|
||||
if err != nil {
|
||||
log.Printf("Error reading directory: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if !file.IsDir() {
|
||||
// Try to send file to connected peers
|
||||
filePath := filepath.Join(s.servePath, file.Name())
|
||||
if err := s.sendFile(filePath); err != nil {
|
||||
log.Printf("Error sending file %s: %v", file.Name(), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *FileServer) sendFile(filePath string) error {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open file: %v", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// Create a destination for the file transfer
|
||||
dest, err := destination.New(
|
||||
s.identity,
|
||||
destination.OUT,
|
||||
destination.SINGLE,
|
||||
APP_NAME,
|
||||
APP_ASPECT,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create destination: %v", err)
|
||||
}
|
||||
|
||||
// Set up link for file transfer
|
||||
callback := func(l interface{}) {
|
||||
if link, ok := l.(*link.Link); ok {
|
||||
// Create a new resource with auto-compression enabled
|
||||
res, err := resource.New(file, true)
|
||||
if err != nil {
|
||||
log.Printf("Error creating resource: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// The filename is automatically set from the file handle
|
||||
// in resource.New when using an io.ReadWriteSeeker
|
||||
|
||||
// Send the resource through the link
|
||||
if err := link.SendResource(res); err != nil {
|
||||
log.Printf("Error sending resource: %v", err)
|
||||
return
|
||||
}
|
||||
log.Printf("File %s sent successfully", filepath.Base(filePath))
|
||||
}
|
||||
}
|
||||
|
||||
dest.SetLinkEstablishedCallback(callback)
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user