Compare commits

...

8 Commits
v2.2.2 ... main

  1. 12
      README.md
  2. 2
      src/addr/local.go
  3. 16
      src/checksum/checksum.go
  4. 2
      src/checksum/checksum_test.go
  5. 2
      src/encryption/decrypt.go
  6. 2
      src/encryption/encrypt.go
  7. 2
      src/encryption/encryption_test.go
  8. 2
      src/encryption/key.go
  9. 67
      src/fsys/dir.go
  10. 38
      src/fsys/dir_test.go
  11. 6
      src/fsys/file.go
  12. 2
      src/fsys/file_test.go
  13. 70
      src/fsys/symlink.go
  14. 42
      src/fsys/symlink_test.go
  15. 4
      src/go.mod
  16. 28
      src/main.go
  17. 97
      src/node/node.go
  18. 10
      src/node/options.go
  19. 2
      src/protocol/constants.go
  20. 9
      src/protocol/headers.go
  21. 4
      src/protocol/packet.go
  22. 4
      src/protocol/packetConstruct.go
  23. 4
      src/protocol/packetDecode.go
  24. 2
      src/protocol/protocol_test.go
  25. 2
      src/protocol/recv.go
  26. 41
      src/protocol/send.go
  27. 1
      src/testfiles/testdir/testsymlink2.txt
  28. 1
      src/testfiles/testsymlink.txt

12
README.md

@ -16,7 +16,7 @@ In order to transfer one file on one computer to another - they need to establis
In order to establish a connection - there needs to be a 1) sender (server) (the owner of the file), waiting for connections, and a 2) receiver (client), who will try to connect to a sender (server). If the requirements are met - client will connect to server and the packet exchange will begin.
The server and the client needs to communicate with packets according to certain rules, given by a [protocol](https://github.com/Unbewohnte/ftu/tree/main/src/protocol).
The server and the client needs to communicate with packets according to certain rules, given by a [protocol](http://unbewohnte.xyz:3000/Unbewohnte/ftu/src/branch/main/src/protocol).
The packet has its header and body. They are divided into several groups of use by headers, this way we can specify what kind of data is stored inside packet`s body and react accordingly.
@ -28,13 +28,13 @@ Thus, with a connection and a way of communication, the sender will send some pa
## ● Installation
### ● From release (Pre-compiled)
- Proceed to [releases page](https://github.com/Unbewohnte/ftu/releases)
- Proceed to [releases page](http://unbewohnte.xyz:3000/Unbewohnte/ftu/releases)
- Choose a version/architecture you have and download an archive
- Unpack an archive
- If on GNU/Linux - run `chmod +x install.sh && sudo ./install.sh`
### ● From source (Compile it yourself) (You need [Go](https://golang.org/dl/) and [git](https://git-scm.com/) to be installed on your machine)
- `git clone https://github.com/Unbewohnte/ftu.git`
- `git clone http://unbewohnte.xyz:3000/Unbewohnte/ftu`
- `cd` into the folder
- If on GNU/Linux - run `make && sudo make install` or `make && chmod +x install.sh && sudo ./install`
- else - cd into src/ folder and simply run `go build`; after that you`re free to put the binary wherever you desire
@ -46,7 +46,7 @@ Now you have ftu installed !
## ● Usage
`ftu -h` - to print a usage message
`ftu [FLAGS]`
`ftu [FLAGs]`
### ● FLAGs
- -p [uint] for port
@ -83,9 +83,5 @@ creates a node that will send every file in the directory !RECUSRIVELY!
---
## ● Inspired by [croc](https://github.com/schollz/croc)
---
## ● License
GPLv3 license

2
src/addr/local.go

@ -1,6 +1,6 @@
/*
ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/))
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu

16
src/checksum/checksum.go

@ -1,6 +1,6 @@
/*
ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/))
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu
@ -21,6 +21,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
package checksum
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"io"
@ -36,8 +37,8 @@ import (
// GetPartialCheckSum is default method used to get a file checksum by sender and receiver
func GetPartialCheckSum(file *os.File) (string, error) {
// "capturing" CHUNKSIZE bytes and then skipping STEP bytes before the next chunk until the last one
const CHUNKS uint = 100
const CHUNKSIZE uint = 100
const CHUNKS uint = 50
const CHUNKSIZE uint = 50
const STEP uint = 250
fileStats, err := file.Stat()
@ -48,7 +49,7 @@ func GetPartialCheckSum(file *os.File) (string, error) {
fileSize := fileStats.Size()
if fileSize < int64(CHUNKS*CHUNKSIZE+STEP*(CHUNKS-1)) {
// file is too small to chop it in chunks, so just doing full checksum
// file is too small to chop it in chunks, so just get the full checksum
checksum, err := getFullCheckSum(file)
if err != nil {
@ -62,19 +63,20 @@ func GetPartialCheckSum(file *os.File) (string, error) {
return "", err
}
var capturedChunks string
// var capturedChunks string
var capturedChunks bytes.Buffer
var read uint64 = 0
for i := 0; uint(i) < CHUNKS; i++ {
buffer := make([]byte, CHUNKSIZE)
r, _ := file.ReadAt(buffer, int64(read))
capturedChunks += string(buffer)
capturedChunks.Write(buffer)
read += uint64(r)
read += uint64(STEP)
}
checksumBytes := sha256.Sum256([]byte(capturedChunks))
checksumBytes := sha256.Sum256(capturedChunks.Bytes())
checksum := hex.EncodeToString(checksumBytes[:])
return checksum, nil

2
src/checksum/checksum_test.go

@ -1,6 +1,6 @@
/*
ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/))
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu

2
src/encryption/decrypt.go

@ -1,6 +1,6 @@
/*
ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/))
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu

2
src/encryption/encrypt.go

@ -1,6 +1,6 @@
/*
ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/))
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu

2
src/encryption/encryption_test.go

@ -1,6 +1,6 @@
/*
ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/))
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu

2
src/encryption/key.go

@ -1,6 +1,6 @@
/*
ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/))
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu

67
src/fsys/dir.go

@ -1,6 +1,6 @@
/*
ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/))
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu
@ -22,6 +22,7 @@ package fsys
import (
"fmt"
"io/fs"
"os"
"path/filepath"
)
@ -32,6 +33,7 @@ type Directory struct {
Path string
Size uint64
RelativeParentPath string // Relative path to the directory, where the highest point in the hierarchy is the upmost parent dir. Set manually
Symlinks []*Symlink
Files []*File
Directories []*Directory
}
@ -63,12 +65,13 @@ func GetDir(path string, recursive bool) (*Directory, error) {
// loop through each entry in the directory
entries, err := os.ReadDir(absPath)
if err != nil {
if err != nil && err != fs.ErrPermission {
return nil, err
}
var innerDirs []*Directory
var innerFiles []*File
var innerSymlinks []*Symlink
for _, entry := range entries {
entryInfo, err := entry.Info()
if err != nil {
@ -92,11 +95,30 @@ func GetDir(path string, recursive bool) (*Directory, error) {
// if not - skip the directory and only work with the files
} else {
// not a directory
switch entryInfo.Mode()&os.ModeSymlink != 0 {
case true:
// it is a symlink
innerSymlinkPath := filepath.Join(absPath, entryInfo.Name())
symlink, err := GetSymlink(innerSymlinkPath, false)
if err != nil {
// skip this symlink
continue
}
innerSymlinks = append(innerSymlinks, symlink)
case false:
// it is a usual file
innerFilePath := filepath.Join(absPath, entryInfo.Name())
innerFile, err := GetFile(innerFilePath)
if err != nil {
return nil, err
// skip this file
continue
}
directory.Size += innerFile.Size
@ -104,9 +126,11 @@ func GetDir(path string, recursive bool) (*Directory, error) {
innerFiles = append(innerFiles, innerFile)
}
}
}
directory.Directories = innerDirs
directory.Files = innerFiles
directory.Symlinks = innerSymlinks
return &directory, nil
}
@ -132,7 +156,27 @@ func (dir *Directory) GetAllFiles(recursive bool) []*File {
return files
}
// Sets `RelativeParentPath` relative to the given base path.
// Returns every symlink in that directory
func (dir *Directory) GetAllSymlinks(recursive bool) []*Symlink {
var symlinks []*Symlink = dir.Symlinks
if recursive {
if len(dir.Directories) == 0 {
return symlinks
}
for _, innerDir := range dir.Directories {
innerSymlinks := innerDir.GetAllSymlinks(recursive)
symlinks = append(symlinks, innerSymlinks...)
}
} else {
symlinks = dir.Symlinks
}
return symlinks
}
// Sets `RelativeParentPath` relative to the given base path for files and `Path`, `TargetPath` for symlinks so the
// file with such path:
// /home/user/directory/somefile.txt
// had a relative path like that:
@ -148,5 +192,20 @@ func (dir *Directory) SetRelativePaths(base string, recursive bool) error {
file.RelativeParentPath = relPath
}
for _, symlink := range dir.GetAllSymlinks(recursive) {
symRelPath, err := filepath.Rel(base, symlink.Path)
if err != nil {
return err
}
symlink.Path = symRelPath
symRelTargetPath, err := filepath.Rel(base, symlink.TargetPath)
if err != nil {
return err
}
symlink.TargetPath = symRelTargetPath
}
return nil
}

38
src/fsys/dir_test.go

@ -1,6 +1,6 @@
/*
ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/))
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu
@ -21,6 +21,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
package fsys
import (
"os"
"path/filepath"
"testing"
)
@ -79,25 +81,23 @@ func Test_GetFiles(t *testing.T) {
}
// func Test_SetRelativePaths(t *testing.T) {
// dirpath := "../testfiles/"
func Test_GetSymlinks(t *testing.T) {
dirpath := "../testfiles/"
// dir, err := GetDir(dirpath, true)
// if err != nil {
// t.Fatalf("%s", err)
// }
os.Symlink(filepath.Join(dirpath, "testfile.txt"), filepath.Join(dirpath, "testsymlink.txt"))
os.Symlink(filepath.Join(dirpath, "testdir", "testfile2.txt"), filepath.Join(dirpath, "testdir", "testsymlink2.txt"))
// absDirPath, err := filepath.Abs(dirpath)
// if err != nil {
// t.Fatalf("%s", err)
// }
dir, err := GetDir(dirpath, true)
if err != nil {
t.Fatalf("%s", err)
}
// err = dir.SetRelativePaths(absDirPath, true)
// if err != nil {
// t.Fatalf("%s", err)
// }
// recursive
symlinks := dir.GetAllSymlinks(true)
symlinkCount := 2
// for count, file := range dir.GetAllFiles(true) {
// t.Errorf("[%d] %v\n", count, file.RelativeParentPath)
// }
// }
if len(symlinks) != symlinkCount {
t.Fatalf("expected to get %d symlinks; got %d\n", symlinkCount, len(symlinks))
}
}

6
src/fsys/file.go

@ -1,6 +1,6 @@
/*
ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/))
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu
@ -25,7 +25,7 @@ import (
"os"
"path/filepath"
"github.com/Unbewohnte/ftu/checksum"
"unbewohnte/ftu/checksum"
)
// A struct that represents the necessary file information for transportation through node
@ -45,7 +45,7 @@ var ErrorNotFile error = fmt.Errorf("not a file")
// Get general information about a file with the
// future ability to open it.
// NOTE that Handler field is nil BY DEFAULT until you
// manually call a (file *File) Open() function to open it !
// manually call (file *File).Open() to open it !
func GetFile(path string) (*File, error) {
absPath, err := filepath.Abs(path)
if err != nil {

2
src/fsys/file_test.go

@ -1,6 +1,6 @@
/*
ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/))
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu

70
src/fsys/symlink.go

@ -0,0 +1,70 @@
/*
ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package fsys
import (
"fmt"
"os"
)
type Symlink struct {
TargetPath string
Path string
}
// Checks whether path is referring to a symlink or not
func IsSymlink(path string) (bool, error) {
stats, err := os.Lstat(path)
if err != nil {
return false, err
}
isSymlink := stats.Mode()&os.ModeSymlink != 0
return isSymlink, nil
}
var ErrorNotSymlink error = fmt.Errorf("not a symlink")
// get necessary information about a symlink in a filesystem. If check is false -
// does not check if path REALLY refers to a symlink
func GetSymlink(path string, check bool) (*Symlink, error) {
if check {
isSymlink, err := IsSymlink(path)
if err != nil {
return nil, err
}
if !isSymlink {
return nil, ErrorNotSymlink
}
}
target, err := os.Readlink(path)
if err != nil {
return nil, err
}
symlink := Symlink{
TargetPath: target,
Path: path,
}
return &symlink, nil
}

42
src/fsys/symlink_test.go

@ -0,0 +1,42 @@
/*
ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package fsys
import (
"os"
"path/filepath"
"testing"
)
func Test_IsSymlink(t *testing.T) {
dirpath := "../testfiles/"
symlinkPath := filepath.Join(dirpath, "testsymlink.txt")
os.Symlink(filepath.Join(dirpath, "testfile.txt"), symlinkPath)
isSymlink, err := IsSymlink(symlinkPath)
if err != nil {
t.Fatalf("%s\n", err)
}
if !isSymlink {
t.Fatalf("%s expected to be a symlink\n", symlinkPath)
}
}

4
src/go.mod

@ -1,3 +1,3 @@
module github.com/Unbewohnte/ftu
module unbewohnte/ftu
go 1.17
go 1.18

28
src/main.go

@ -1,6 +1,6 @@
/*
ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/))
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte (https://unbewohnte.xyz/))
This file is a part of ftu
@ -26,13 +26,13 @@ import (
"fmt"
"os"
"github.com/Unbewohnte/ftu/node"
"unbewohnte/ftu/node"
)
var (
VERSION string = "v2.2.2"
VERSION string = "v2.3.3"
versionInformation string = fmt.Sprintf("ftu %s\n\nCopyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/))\nThis program comes with ABSOLUTELY NO WARRANTY.\nThis is free software, and you are welcome to redistribute it under certain conditions; type \"ftu -l\" for details.\n", VERSION)
versionInformation string = fmt.Sprintf("ftu %s\nfile transferring utility\n\nCopyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)\nThis program comes with ABSOLUTELY NO WARRANTY.\nThis is free software, and you are welcome to redistribute it under certain conditions; type \"ftu -l\" for details.\n", VERSION)
//go:embed COPYING
licenseInformation string
@ -52,15 +52,15 @@ var (
func init() {
flag.Usage = func() {
fmt.Printf("ftu -[FLAG]...\n\n")
fmt.Printf("ftu -[FLAGs]\n\n")
fmt.Printf("[FLAGs]\n\n")
fmt.Printf("| -p [Uinteger_here] for port\n")
fmt.Printf("| -r [true|false] for recursive sending of a directory\n")
fmt.Printf("| -p [integer] for port\n")
fmt.Printf("| -r [true|false] send recursively or not\n")
fmt.Printf("| -a [ip_address|domain_name] address to connect to (cannot be used with -s)\n")
fmt.Printf("| -d [path_to_directory] where the files will be downloaded to (cannot be used with -s)\n")
fmt.Printf("| -s [path_to_file|directory] to send it (cannot be used with -a)\n")
fmt.Printf("| -? [true|false] to turn on|off verbose output\n")
fmt.Printf("| -s [path_to_file|directory] send it (cannot be used with -a)\n")
fmt.Printf("| -? [true|false] turn on|off verbose output\n")
fmt.Printf("| -l print license information\n")
fmt.Printf("| -v print version information\n\n\n")
@ -98,12 +98,12 @@ func init() {
// validate flags
if *SEND == "" && *ADDRESS == "" {
fmt.Printf("Neither sending nor receiving flag was specified. Run ftu -h for help\n")
fmt.Printf("[ERROR] Neither sending nor receiving flag was specified. Run ftu -h for help\n")
os.Exit(-1)
}
if *SEND != "" && *ADDRESS != "" {
fmt.Printf("Can`t send and receive at the same time. Specify either -s or -a\n")
fmt.Printf("[ERROR] Can't send and receive at the same time. Specify either -s or -a\n")
os.Exit(-1)
}
@ -122,11 +122,11 @@ func main() {
VerboseOutput: *VERBOSE,
IsSending: isSending,
WorkingPort: *PORT,
ServerSide: &node.ServerSideNodeOptions{
SenderSide: &node.SenderNodeOptions{
ServingPath: *SEND,
Recursive: *RECUSRIVE,
},
ClientSide: &node.ClientSideNodeOptions{
ReceiverSide: &node.ReceiverNodeOptions{
ConnectionAddr: *ADDRESS,
DownloadsFolderPath: *DOWNLOADS_DIR,
},
@ -134,7 +134,7 @@ func main() {
node, err := node.NewNode(&nodeOptions)
if err != nil {
fmt.Printf("Error constructing a new node: %s\n", err)
fmt.Printf("[ERROR] Error constructing a new node: %s\n", err)
os.Exit(-1)
}

97
src/node/node.go

@ -1,6 +1,6 @@
/*
ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/))
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu
@ -32,11 +32,11 @@ import (
"fmt"
"github.com/Unbewohnte/ftu/addr"
"github.com/Unbewohnte/ftu/checksum"
"github.com/Unbewohnte/ftu/encryption"
"github.com/Unbewohnte/ftu/fsys"
"github.com/Unbewohnte/ftu/protocol"
"unbewohnte/ftu/addr"
"unbewohnte/ftu/checksum"
"unbewohnte/ftu/encryption"
"unbewohnte/ftu/fsys"
"unbewohnte/ftu/protocol"
)
// netInfowork specific settings
@ -56,9 +56,11 @@ type sending struct {
AllowedToTransfer bool // the way to notify the mainloop of a sending node to start sending pieces of files
InTransfer bool // already transferring|receiving files
FilesToSend []*fsys.File
SymlinksToSend []*fsys.Symlink
CurrentFileID uint64 // an id of a file that is currently being transported
SentBytes uint64 // how many bytes sent already
TotalTransferSize uint64 // how many bytes will be sent in total
CurrentSymlinkIndex uint64 // current index of a symlink that is
}
// Receiving-side node information
@ -91,7 +93,7 @@ func NewNode(options *NodeOptions) (*Node, error) {
var isDir bool
if options.IsSending {
// sending node preparation
sendingPathStats, err := os.Stat(options.ServerSide.ServingPath)
sendingPathStats, err := os.Stat(options.SenderSide.ServingPath)
if err != nil {
return nil, err
}
@ -106,12 +108,12 @@ func NewNode(options *NodeOptions) (*Node, error) {
} else {
// receiving node preparation
var err error
options.ClientSide.DownloadsFolderPath, err = filepath.Abs(options.ClientSide.DownloadsFolderPath)
options.ReceiverSide.DownloadsFolderPath, err = filepath.Abs(options.ReceiverSide.DownloadsFolderPath)
if err != nil {
return nil, err
}
err = os.MkdirAll(options.ClientSide.DownloadsFolderPath, os.ModePerm)
err = os.MkdirAll(options.ReceiverSide.DownloadsFolderPath, os.ModePerm)
if err != nil {
return nil, err
}
@ -125,22 +127,22 @@ func NewNode(options *NodeOptions) (*Node, error) {
isSending: options.IsSending,
netInfo: &netInfo{
Port: options.WorkingPort,
ConnAddr: options.ClientSide.ConnectionAddr,
ConnAddr: options.ReceiverSide.ConnectionAddr,
EncryptionKey: nil,
Conn: nil,
},
stopped: false,
transferInfo: &transferInfo{
Sending: &sending{
ServingPath: options.ServerSide.ServingPath,
Recursive: options.ServerSide.Recursive,
ServingPath: options.SenderSide.ServingPath,
Recursive: options.SenderSide.Recursive,
IsDirectory: isDir,
TotalTransferSize: 0,
SentBytes: 0,
},
Receiving: &receiving{
AcceptedFiles: nil,
DownloadsPath: options.ClientSide.DownloadsFolderPath,
DownloadsPath: options.ReceiverSide.DownloadsFolderPath,
ReceivedBytes: 0,
TotalDownloadSize: 0,
},
@ -323,7 +325,7 @@ func (node *Node) send() {
incomingPacket, ok := <-node.packetPipe
if !ok {
fmt.Printf("\nThe connection has been closed unexpectedly\n")
os.Exit(-1.)
os.Exit(-1)
}
// if encryption key is set - decrypt packet on the spot
@ -355,8 +357,10 @@ func (node *Node) send() {
panic(err)
}
filesToSend := DIRTOSEND.GetAllFiles(node.transferInfo.Sending.Recursive)
symlinksToSend := DIRTOSEND.GetAllSymlinks(node.transferInfo.Sending.Recursive)
node.transferInfo.Sending.SymlinksToSend = symlinksToSend
// notify the other node about all the files that are going to be sent
for counter, file := range filesToSend {
// assign ID and add it to the node sendlist
file.ID = uint64(counter)
@ -410,7 +414,14 @@ func (node *Node) send() {
// Transfer section
if len(node.transferInfo.Sending.FilesToSend) == 0 {
// if all files have been sent -> send symlinks
if len(node.transferInfo.Sending.FilesToSend) == 0 && node.transferInfo.Sending.CurrentSymlinkIndex < uint64(len(node.transferInfo.Sending.SymlinksToSend)) {
protocol.SendSymlink(node.transferInfo.Sending.SymlinksToSend[node.transferInfo.Sending.CurrentSymlinkIndex], node.netInfo.Conn, encrKey)
node.transferInfo.Sending.CurrentSymlinkIndex++
continue
}
if len(node.transferInfo.Sending.FilesToSend) == 0 && node.transferInfo.Sending.CurrentSymlinkIndex == uint64(len(node.transferInfo.Sending.SymlinksToSend)) {
// if there`s nothing else to send - create and send DONE packet
protocol.SendPacket(node.netInfo.Conn, protocol.Packet{
Header: protocol.HeaderDone,
@ -512,7 +523,7 @@ func (node *Node) send() {
default:
node.stopped = true
fmt.Printf("\nAn error occured while sending a piece of \"%s\": %s", node.transferInfo.Sending.FilesToSend[currentFileIndex].Name, err)
fmt.Printf("\n[ERROR] An error occured while sending a piece of \"%s\": %s", node.transferInfo.Sending.FilesToSend[currentFileIndex].Name, err)
panic(err)
}
}
@ -525,7 +536,7 @@ func (node *Node) receive() {
// connect to the sending node
err := node.connect()
if err != nil {
fmt.Printf("\nCould not connect to %s:%d", node.netInfo.ConnAddr, node.netInfo.Port)
fmt.Printf("\n[ERROR] Could not connect to %s:%d\n", node.netInfo.ConnAddr, node.netInfo.Port)
os.Exit(-1)
}
@ -763,11 +774,12 @@ func (node *Node) receive() {
// accepted
// append provided bytes to the file
if acceptedFile.Handler == nil {
err = acceptedFile.Open()
if err != nil {
panic(err)
}
}
fileBytes := fileBytesBuffer.Bytes()
@ -777,11 +789,6 @@ func (node *Node) receive() {
}
acceptedFile.SentBytes += uint64(wrote)
node.transferInfo.Receiving.ReceivedBytes += uint64(wrote)
err = acceptedFile.Close()
if err != nil {
panic(err)
}
}
}
@ -808,10 +815,12 @@ func (node *Node) receive() {
fmt.Printf("\n[File] fully received \"%s\" -- %d bytes", acceptedFile.Name, acceptedFile.Size)
}
if acceptedFile.Handler == nil {
err = acceptedFile.Open()
if err != nil {
panic(err)
}
}
// remove this file from the pool
node.transferInfo.Receiving.AcceptedFiles = append(node.transferInfo.Receiving.AcceptedFiles[:index], node.transferInfo.Receiving.AcceptedFiles[index+1:]...)
@ -823,7 +832,10 @@ func (node *Node) receive() {
}
if realChecksum != acceptedFile.Checksum {
fmt.Printf("\n| \"%s\" is corrupted", acceptedFile.Name)
if node.verboseOutput {
fmt.Printf("\n[ERROR] \"%s\" is corrupted", acceptedFile.Name)
}
acceptedFile.Close()
break
} else {
@ -854,6 +866,41 @@ func (node *Node) receive() {
fmt.Printf("\nGot an encryption key: %s", encrKey)
case protocol.HeaderSymlink:
// SYMLINK~(string size in binary)(location in the filesystem)(string size in binary)(location of a target)
packetReader := bytes.NewReader(incomingPacket.Body)
// extract the location of the symlink
var locationSize uint64
binary.Read(packetReader, binary.BigEndian, &locationSize)
symlinkLocationBytes := make([]byte, locationSize)
packetReader.Read(symlinkLocationBytes)
// extract the target of a symlink
var targetSize uint64
binary.Read(packetReader, binary.BigEndian, &targetSize)
symlinkTargetLocationBytes := make([]byte, targetSize)
packetReader.Read(symlinkTargetLocationBytes)
symlinkLocation := string(symlinkLocationBytes)
symlinkTargetLocation := string(symlinkTargetLocationBytes)
// create a symlink
// should be already downloaded
symlinkDir := filepath.Join(node.transferInfo.Receiving.DownloadsPath, filepath.Dir(symlinkLocation))
os.MkdirAll(symlinkDir, os.ModePerm)
os.Symlink(
filepath.Join(node.transferInfo.Receiving.DownloadsPath, symlinkTargetLocation),
filepath.Join(node.transferInfo.Receiving.DownloadsPath, symlinkLocation))
protocol.SendPacket(node.netInfo.Conn, protocol.Packet{
Header: protocol.HeaderReady,
})
case protocol.HeaderDone:
node.mutex.Lock()
node.stopped = true

10
src/node/options.go

@ -1,6 +1,6 @@
/*
ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/))
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu
@ -20,12 +20,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
package node
type ServerSideNodeOptions struct {
type SenderNodeOptions struct {
ServingPath string
Recursive bool
}
type ClientSideNodeOptions struct {
type ReceiverNodeOptions struct {
ConnectionAddr string
DownloadsFolderPath string
}
@ -35,6 +35,6 @@ type NodeOptions struct {
IsSending bool
WorkingPort uint
VerboseOutput bool
ServerSide *ServerSideNodeOptions
ClientSide *ClientSideNodeOptions
SenderSide *SenderNodeOptions
ReceiverSide *ReceiverNodeOptions
}

2
src/protocol/constants.go

@ -1,6 +1,6 @@
/*
ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/))
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu

9
src/protocol/headers.go

@ -1,6 +1,6 @@
/*
ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/))
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu
@ -114,3 +114,10 @@ const HeaderDirectory Header = "DIRECTORY"
// Body must contain a file ID.
// ie: ALREADYHAVE~(file ID in binary)
const HeaderAlreadyHave Header = "ALREADYHAVE"
// SYMLINK
// Sent by sender AFTER ALL FILES has been sent already. Indicates that there
// is a symlink in some place that points to some other already received file.
// Body must contain information where the symlink is and the target file.
// ie: SYMLINK~(string size in binary)(location in the filesystem)(string size in binary)(location of a target)
const HeaderSymlink Header = "SYMLINK"

4
src/protocol/packet.go

@ -1,6 +1,6 @@
/*
ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/))
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu
@ -31,7 +31,7 @@ import (
"fmt"
"strings"
"github.com/Unbewohnte/ftu/encryption"
"unbewohnte/ftu/encryption"
)
// Internal representation of packet before|after the transportation

4
src/protocol/packetConstruct.go

@ -1,6 +1,6 @@
/*
ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/))
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu
@ -25,7 +25,7 @@ import (
"bytes"
"encoding/binary"
"github.com/Unbewohnte/ftu/fsys"
"unbewohnte/ftu/fsys"
)
// constructs a ready to send FILE packet

4
src/protocol/packetDecode.go

@ -1,6 +1,6 @@
/*
ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/))
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu
@ -26,7 +26,7 @@ import (
"encoding/binary"
"fmt"
"github.com/Unbewohnte/ftu/fsys"
"unbewohnte/ftu/fsys"
)
var ErrorWrongPacket error = fmt.Errorf("wrong type of packet header")

2
src/protocol/protocol_test.go

@ -1,6 +1,6 @@
/*
ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/))
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu

2
src/protocol/recv.go

@ -1,6 +1,6 @@
/*
ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/))
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu

41
src/protocol/send.go

@ -1,6 +1,6 @@
/*
ftu - file transferring utility.
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeevich (Unbewohnte (https://unbewohnte.xyz/))
Copyright (C) 2021,2022 Kasyanov Nikolay Alexeyevich (Unbewohnte)
This file is a part of ftu
@ -28,8 +28,8 @@ import (
"io"
"net"
"github.com/Unbewohnte/ftu/encryption"
"github.com/Unbewohnte/ftu/fsys"
"unbewohnte/ftu/encryption"
"unbewohnte/ftu/fsys"
)
// Sends given packet to connection.
@ -174,7 +174,7 @@ func SendPiece(file *fsys.File, connection net.Conn, encrKey []byte) (uint64, er
if encrKey != nil {
// account for padding
canSendBytes -= 32
canSendBytes -= 48
}
if (file.Size - file.SentBytes) < canSendBytes {
@ -209,3 +209,36 @@ func SendPiece(file *fsys.File, connection net.Conn, encrKey []byte) (uint64, er
return sentBytes, nil
}
// Sends a symlink to the other side. If encrKey is not nil - encrypts the packet with this key
func SendSymlink(symlink *fsys.Symlink, connection net.Conn, encrKey []byte) error {
symlinkPacket := Packet{
Header: HeaderSymlink,
}
symlinkPacketBodyBuff := new(bytes.Buffer)
// SYMLINK~(string size in binary)(location in the filesystem)(string size in binary)(location of a target)
binary.Write(symlinkPacketBodyBuff, binary.BigEndian, uint64(len(symlink.Path)))
symlinkPacketBodyBuff.Write([]byte(symlink.Path))
binary.Write(symlinkPacketBodyBuff, binary.BigEndian, uint64(len(symlink.TargetPath)))
symlinkPacketBodyBuff.Write([]byte(symlink.TargetPath))
symlinkPacket.Body = symlinkPacketBodyBuff.Bytes()
if encrKey != nil {
err := symlinkPacket.EncryptBody(encrKey)
if err != nil {
return err
}
}
err := SendPacket(connection, symlinkPacket)
if err != nil {
return err
}
return nil
}

1
src/testfiles/testdir/testsymlink2.txt

@ -0,0 +1 @@
../testfiles/testdir/testfile2.txt

1
src/testfiles/testsymlink.txt

@ -0,0 +1 @@
../testfiles/testfile.txt
Loading…
Cancel
Save