@ -1,6 +1,6 @@
/ *
ftu - file transferring utility .
Copyright ( C ) 2021 Kasyanov Nikolay Alexeevich ( Unbewohnte ( https : //unbewohnte.xyz/))
Copyright ( C ) 2021 , 2022 Kasyanov Nikolay Alexeyevich ( Unbewohnte )
This file is a part of ftu
@ -32,21 +32,15 @@ import (
"fmt"
"gith ub.com/U nbewohnte/ftu/addr"
"gith ub.com/U nbewohnte/ftu/checksum"
"gith ub.com/U nbewohnte/ftu/encryption"
"gith ub.com/U nbewohnte/ftu/fsys"
"gith ub.com/U nbewohnte/ftu/protocol"
"unbewohnte/ftu/addr"
"unbewohnte/ftu/checksum"
"unbewohnte/ftu/encryption"
"unbewohnte/ftu/fsys"
"unbewohnte/ftu/protocol"
)
// node-controlling states
type nodeInnerstates struct {
Stopped bool // the way to exit the mainloop in case of an external error or a successful end of a transfer
AllowedToTransfer bool // the way to notify the mainloop of a sending node to start sending pieces of files
}
// netInfowork specific settings
type netInfoInfo struct {
type netInfo struct {
ConnAddr string // address to connect to. Does not include port
Conn net . Conn // the core TCP connection of the node. Self-explanatory
Port uint // a port to connect to/listen on
@ -59,15 +53,22 @@ type sending struct {
IsDirectory bool // is ServingPath a directory
Recursive bool // recursively send directory
CanSendBytes bool // is the other node ready to receive another piece
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
// CurrentFileIndex uint64 // an index of a file with a specific ID that is currently being transported
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
type receiving struct {
AcceptedFiles [ ] * fsys . File // files that`ve been accepted to be received
DownloadsPath string // where to download
TotalDownloadSize uint64 // how many bytes will be received in total
ReceivedBytes uint64 // how many bytes downloaded so far
}
// Both sending-side and receiving-side information
@ -78,11 +79,12 @@ type transferInfo struct {
// Sender and receiver in one type !
type Node struct {
verboseOutput bool
mutex * sync . Mutex
packetPipe chan * protocol . Packet // a way to receive incoming packets from another goroutine
isSending bool // sending or a receiving node
netInfo * netInfoInfo
state * nodeInnerstates
stopped bool // the way to exit the mainloop in case of an external error or a successful end of a transfer
netInfo * netInfo
transferInfo * transferInfo
}
@ -91,7 +93,7 @@ func NewNode(options *NodeOptions) (*Node, error) {
var isDir bool
if options . IsSending {
// sending node preparation
sendingPathStats , err := os . Stat ( options . Serv erSide . ServingPath )
sendingPathStats , err := os . Stat ( options . Send erSide . 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 . Client Side. DownloadsFolderPath , err = filepath . Abs ( options . Client Side. DownloadsFolderPath )
options . Receiver Side. DownloadsFolderPath , err = filepath . Abs ( options . Receiver Side. DownloadsFolderPath )
if err != nil {
return nil , err
}
err = os . MkdirAll ( options . Client Side. DownloadsFolderPath , os . ModePerm )
err = os . MkdirAll ( options . Receiver Side. DownloadsFolderPath , os . ModePerm )
if err != nil {
return nil , err
}
@ -119,28 +121,30 @@ func NewNode(options *NodeOptions) (*Node, error) {
}
node := Node {
verboseOutput : options . VerboseOutput ,
mutex : & sync . Mutex { } ,
packetPipe : make ( chan * protocol . Packet , 100 ) ,
isSending : options . IsSending ,
netInfo : & netInfoInfo {
netInfo : & netInfo {
Port : options . WorkingPort ,
ConnAddr : options . Client Side. ConnectionAddr ,
ConnAddr : options . Receiver Side. ConnectionAddr ,
EncryptionKey : nil ,
Conn : nil ,
} ,
state : & nodeInnerstates {
AllowedToTransfer : false ,
Stopped : false ,
} ,
stopped : false ,
transferInfo : & transferInfo {
Sending : & sending {
ServingPath : options . Serv erSide . ServingPath ,
Recursive : options . Serv erSide . Recursive ,
ServingPath : options . SenderSide . ServingPath ,
Recursive : options . Send erSide . Recursive ,
IsDirectory : isDir ,
TotalTransferSize : 0 ,
SentBytes : 0 ,
} ,
Receiving : & receiving {
AcceptedFiles : nil ,
DownloadsPath : options . ClientSide . DownloadsFolderPath ,
DownloadsPath : options . ReceiverSide . DownloadsFolderPath ,
ReceivedBytes : 0 ,
TotalDownloadSize : 0 ,
} ,
} ,
}
@ -153,14 +157,14 @@ func (node *Node) connect() error {
node . netInfo . Port = 7270
}
fmt . Printf ( "Connecting to %s:%d...\n " , node . netInfo . ConnAddr , node . netInfo . Port )
fmt . Printf ( "\n Connecting to %s:%d..." , node . netInfo . ConnAddr , node . netInfo . Port )
conn , err := net . DialTimeout ( "tcp" , fmt . Sprintf ( "%s:%d" , node . netInfo . ConnAddr , node . netInfo . Port ) , time . Second * 5 )
if err != nil {
return err
}
fmt . Printf ( "Connected\n " )
fmt . Printf ( "\n Connected" )
node . netInfo . Conn = conn
@ -183,7 +187,7 @@ func (node *Node) disconnect() error {
return err
}
node . state . St opped = true
node . stopped = true
}
return nil
@ -202,17 +206,38 @@ func (node *Node) waitForConnection() error {
return err
}
fmt . Printf ( "New connection from %s\n " , connection . RemoteAddr ( ) . String ( ) )
fmt . Printf ( "\n New connection from %s" , connection . RemoteAddr ( ) . String ( ) )
node . netInfo . Conn = connection
return nil
}
// Starts the node in either sending or receiving state and performs the transfer
func ( node * Node ) Start ( ) {
// Prints information about the transfer after defined delay
func ( node * Node ) printTransferInfo ( delay time . Duration ) error {
time . Sleep ( delay )
switch node . isSending {
case true :
if ! node . transferInfo . Sending . AllowedToTransfer {
// do not print if the transfer has not been accepted yet
break
}
fmt . Printf ( "\r| (%.2f/%.2f MB)" ,
float32 ( node . transferInfo . Sending . SentBytes ) / 1024 / 1024 ,
float32 ( node . transferInfo . Sending . TotalTransferSize ) / 1024 / 1024 ,
)
case false :
fmt . Printf ( "\r| (%.2f/%.2f MB)" ,
float32 ( node . transferInfo . Receiving . ReceivedBytes ) / 1024 / 1024 ,
float32 ( node . transferInfo . Receiving . TotalDownloadSize ) / 1024 / 1024 ,
)
}
return nil
}
func ( node * Node ) send ( ) {
// SENDER NODE
localIP , err := addr . GetLocal ( )
@ -221,40 +246,44 @@ func (node *Node) Start() {
}
// retrieve information about the file|directory
var fileToSend * fsys . File
var dirToSend * fsys . Directory
var FILETOSEND * fsys . File
var DIRTOSEND * fsys . Directory
switch node . transferInfo . Sending . IsDirectory {
case true :
dirToSend , err = fsys . GetDir ( node . transferInfo . Sending . ServingPath , node . transferInfo . Sending . Recursive )
DIRTOSEND , err = fsys . GetDir ( node . transferInfo . Sending . ServingPath , node . transferInfo . Sending . Recursive )
if err != nil {
panic ( err )
}
case false :
fileToSend , err = fsys . GetFile ( node . transferInfo . Sending . ServingPath )
FILETOSEND , err = fsys . GetFile ( node . transferInfo . Sending . ServingPath )
if err != nil {
panic ( err )
}
}
if dirToSend != nil {
size := float32 ( dirToSend . Size ) / 1024 / 1024
if DIRTOSEND != nil {
node . transferInfo . Sending . TotalTransferSize = DIRTOSEND . Size
displaySize := float32 ( DIRTOSEND . Size ) / 1024 / 1024
sizeLevel := "MiB"
if size >= 1024 {
if di splayS ize >= 1024 {
// GiB
size = size / 1024
di splayS ize = di splayS ize / 1024
sizeLevel = "GiB"
}
fmt . Printf ( "Sending \"%s\" (%.3f %s) locally on %s:%d\n" , dirToSend . Name , size , sizeLevel , localIP , node . netInfo . Port )
fmt . Printf ( "\nSending \"%s\" (%.3f %s) locally on %s:%d and remotely (if configured)" , DIRTOSEND . Name , di splayS ize, sizeLevel , localIP , node . netInfo . Port )
} else {
size := float32 ( fileToSend . Size ) / 1024 / 1024
node . transferInfo . Sending . TotalTransferSize = FILETOSEND . Size
displaySize := float32 ( FILETOSEND . Size ) / 1024 / 1024
sizeLevel := "MiB"
if size >= 1024 {
if di splayS ize >= 1024 {
// GiB
size = size / 1024
di splayS ize = di splayS ize / 1024
sizeLevel = "GiB"
}
fmt . Printf ( "Sending \"%s\" (%.3f %s) locally on %s:%d\n" , fileToSend . Name , size , sizeLevel , localIP , node . netInfo . Port )
fmt . Printf ( "\nSending \"%s\" (%.3f %s) locally on %s:%d and remotely (if configured)" , FILETOSEND . Name , di splayS ize, sizeLevel , localIP , node . netInfo . Port )
}
@ -267,7 +296,7 @@ func (node *Node) Start() {
// generate and send encryption key
encrKey := encryption . Generate32AESkey ( )
node . netInfo . EncryptionKey = encrKey
fmt . Printf ( "Generated encryption key: %s\n" , encrKey )
fmt . Printf ( "\n Generated encryption key: %s\n" , encrKey )
err = protocol . SendEncryptionKey ( node . netInfo . Conn , encrKey )
if err != nil {
@ -278,20 +307,25 @@ func (node *Node) Start() {
go protocol . ReceivePackets ( node . netInfo . Conn , node . packetPipe )
// send info about file/directory
go protocol . SendTransferOffer ( node . netInfo . Conn , fileToSend , dirToSend , node . netInfo . EncryptionKey )
go protocol . SendTransferOffer ( node . netInfo . Conn , FILETOSEND , DIRTOSEND , node . netInfo . EncryptionKey )
// mainloop
for {
if node . state . Stopped {
if node . stopped {
fmt . Printf ( "\n" )
node . disconnect ( )
break
}
if ! node . verboseOutput {
go node . printTransferInfo ( time . Second )
}
// receive incoming packets and decrypt them if necessary
incomingPacket , ok := <- node . packetPipe
if ! ok {
fmt . Printf ( "The connection has been closed unexpectedly\n" )
os . Exit ( - 1. )
fmt . Printf ( "\n The connection has been closed unexpectedly\n" )
os . Exit ( - 1 )
}
// if encryption key is set - decrypt packet on the spot
@ -311,89 +345,47 @@ func (node *Node) Start() {
case protocol . HeaderAccept :
// the receiving node has accepted the transfer
node . state . AllowedToTransfer = true
fmt . Printf ( "Transfer allowed. Sending...\n" )
node . transferInfo . Sending . AllowedToTransfer = true
// notify it about all the files that are going to be sent
// prepare files to send
switch node . transferInfo . Sending . IsDirectory {
case true :
// send file packets for the files in the directory
err = dirToSend . SetRelativePaths ( dirToSend . Path , node . transferInfo . Sending . Recursive )
err = DIRTOSEND . SetRelativePaths ( DIRTOSEND . Path , node . transferInfo . Sending . Recursive )
if err != nil {
panic ( err )
}
filesToSend := dirToSend . GetAllFiles ( node . transferInfo . Sending . Recursive )
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 )
node . transferInfo . Sending . FilesToSend = append ( node . transferInfo . Sending . FilesToSend , file )
}
// set current file id to the first file
node . transferInfo . Sending . CurrentFileID = 0
filePacket , err := protocol . CreateFilePacket ( file )
if err != nil {
panic ( err )
}
// encrypt if necessary
if node . netInfo . EncryptionKey != nil {
encryptedBody , err := encryption . Encrypt ( node . netInfo . EncryptionKey , filePacket . Body )
if err != nil {
panic ( err )
}
filePacket . Body = encryptedBody
}
err = protocol . SendPacket ( node . netInfo . Conn , * filePacket )
if err != nil {
panic ( err )
}
}
case false :
// send a filepacket of a single file
fileToSend . ID = 0
node . transferInfo . Sending . FilesToSend = append ( node . transferInfo . Sending . FilesToSend , fileToSend )
FILETOSEND . ID = 0
node . transferInfo . Sending . FilesToSend = append ( node . transferInfo . Sending . FilesToSend , FILETOSEND )
// set current file index to the first and only file
node . transferInfo . Sending . CurrentFileID = 0
filePacket , err := protocol . CreateFilePacket ( node . transferInfo . Sending . FilesToSend [ 0 ] )
if err != nil {
panic ( err )
}
// encrypt if necessary
if node . netInfo . EncryptionKey != nil {
encryptedBody , err := encryption . Encrypt ( node . netInfo . EncryptionKey , filePacket . Body )
if err != nil {
panic ( err )
}
filePacket . Body = encryptedBody
}
err = protocol . SendPacket ( node . netInfo . Conn , * filePacket )
if err != nil {
panic ( err )
}
}
fmt . Printf ( "\n" )
case protocol . HeaderReject :
node . state . Stopped = true
fmt . Printf ( "Transfer rejected. Disconnecting...\n" )
node . stopped = true
fmt . Printf ( "\nTransfer rejected. Disconnecting..." )
case protocol . HeaderDisconnecting :
node . state . S topped = true
fmt . Printf ( "%s disconnected\n " , node . netInfo . Conn . RemoteAddr ( ) )
node . stopped = true
fmt . Printf ( "\n%s disconnected" , node . netInfo . Conn . RemoteAddr ( ) )
case protocol . HeaderAlreadyHave :
// the other node already has a file with such ID.
@ -408,30 +400,75 @@ func (node *Node) Start() {
node . transferInfo . Sending . FilesToSend = append ( node . transferInfo . Sending . FilesToSend [ : index ] , node . transferInfo . Sending . FilesToSend [ index + 1 : ] ... )
node . transferInfo . Sending . CurrentFileID ++
node . transferInfo . Sending . SentBytes += fileToSend . Size
node . transferInfo . Sending . InTransfer = false
if node . verboseOutput {
fmt . Printf ( "\n[File] receiver already has \"%s\"" , fileToSend . Name )
}
}
}
}
// 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 ,
} )
fmt . Printf ( "Transfer ended successfully\n" )
node . stopped = true
continue
}
if node . transferInfo . Sending . AllowedToTransfer && ! node . transferInfo . Sending . InTransfer {
// notify the node about the next file to be sent
// determine an index of a file with current ID
var currentFileIndex uint64 = 0
for index , fileToSend := range node . transferInfo . Sending . FilesToSend {
if fileToSend . ID == node . transferInfo . Sending . CurrentFileID {
currentFileIndex = uint64 ( index )
break
}
}
fpacket , err := protocol . CreateFilePacket ( node . transferInfo . Sending . FilesToSend [ currentFileIndex ] )
if err != nil {
panic ( err )
}
if node . netInfo . EncryptionKey != nil {
err = fpacket . EncryptBody ( node . netInfo . EncryptionKey )
if err != nil {
panic ( err )
}
}
node . state . Stopped = true
err = protocol . SendPacket ( node . netInfo . Conn , * fpacket )
if err != nil {
panic ( err )
}
// initiate the transfer for this file on the next iteration
node . transferInfo . Sending . InTransfer = true
continue
}
// if allowed to transfer and the other node is ready to receive packets - send one piece
// and wait for it to be ready again
if node . state . AllowedToTransfer && node . transferInfo . Sending . CanSendBytes {
if node . transferInfo . Sending . AllowedToTransfer && node . transferInfo . Sending . CanSendBytes && node . transferInfo . Sending . InTransfer {
// sending a piece of a single file
// determine an index of a file with current ID
@ -443,10 +480,16 @@ func (node *Node) Start() {
}
}
err = protocol . SendPiece ( node . transferInfo . Sending . FilesToSend [ currentFileIndex ] , node . netInfo . Conn , node . netInfo . EncryptionKey )
sentBytes , err := protocol . SendPiece ( node . transferInfo . Sending . FilesToSend [ currentFileIndex ] , node . netInfo . Conn , node . netInfo . EncryptionKey )
node . transferInfo . Sending . SentBytes += sentBytes
switch err {
case protocol . ErrorSentAll :
// the file has been sent fully
if node . verboseOutput {
fmt . Printf ( "\n[File] fully sent \"%s\" -- %d bytes" , node . transferInfo . Sending . FilesToSend [ currentFileIndex ] . Name , node . transferInfo . Sending . FilesToSend [ currentFileIndex ] . Size )
}
fileIDBuff := new ( bytes . Buffer )
err = binary . Write ( fileIDBuff , binary . BigEndian , node . transferInfo . Sending . FilesToSend [ currentFileIndex ] . ID )
if err != nil {
@ -467,31 +510,33 @@ func (node *Node) Start() {
protocol . SendPacket ( node . netInfo . Conn , endFilePacket )
// remove this file from the queue
node . transferInfo . Sending . FilesToSend = append ( node . transferInfo . Sending . FilesToSend [ : currentFileIndex ] , node . transferInfo . Sending . FilesToSend [ currentFileIndex + 1 : ] ... )
// start sending the next file
// set counter to the next file ID
node . transferInfo . Sending . CurrentFileID ++
node . transferInfo . Sending . InTransfer = false
case nil :
node . transferInfo . Sending . CanSendBytes = false
default :
node . state . S topped = true
node . stopped = true
fmt . Printf ( "An error occured while sending a piece of \"%s\": %s\n " , 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 )
}
}
}
}
case false :
func ( node * Node ) receive ( ) {
// RECEIVER NODE
// connect to the sending node
err := node . connect ( )
if err != nil {
fmt . Printf ( "Could not connect to %s:%d\n" , 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 )
}
@ -501,18 +546,23 @@ func (node *Node) Start() {
// mainloop
for {
node . mutex . Lock ( )
stopped := node . state . S topped
stopped := node . stopped
node . mutex . Unlock ( )
if stopped {
fmt . Printf ( "\n" )
node . disconnect ( )
break
}
if ! node . verboseOutput && node . transferInfo . Receiving . ReceivedBytes != 0 {
go node . printTransferInfo ( time . Second )
}
// receive incoming packets and decrypt them if necessary
incomingPacket , ok := <- node . packetPipe
if ! ok {
fmt . Printf ( "The connection has been closed unexpectedly\n" )
fmt . Printf ( "\nC onnection has been closed unexpectedly\n" )
os . Exit ( - 1 )
}
@ -536,6 +586,8 @@ func (node *Node) Start() {
}
if file != nil {
node . transferInfo . Receiving . TotalDownloadSize = file . Size
size := float32 ( file . Size ) / 1024 / 1024
sizeLevel := "MiB"
if size >= 1024 {
@ -544,7 +596,10 @@ func (node *Node) Start() {
sizeLevel = "GiB"
}
fmt . Printf ( "\n| Filename: %s\n| Size: %.3f %s\n| Checksum: %s\n" , file . Name , size , sizeLevel , file . Checksum )
} else if dir != nil {
node . transferInfo . Receiving . TotalDownloadSize = dir . Size
size := float32 ( dir . Size ) / 1024 / 1024
sizeLevel := "MiB"
if size >= 1024 {
@ -568,7 +623,7 @@ func (node *Node) Start() {
err = os . MkdirAll ( filepath . Join ( node . transferInfo . Receiving . DownloadsPath , dir . Name ) , os . ModePerm )
if err != nil {
// well, just download all files in the default downloads folder then
fmt . Printf ( "[ERROR] could not create a directory\ n" )
fmt . Printf ( "\n[ERROR] could not create a directory, downloading directly to the specified locatio n" )
} else {
// also download everything in a newly created directory
node . transferInfo . Receiving . DownloadsPath = filepath . Join ( node . transferInfo . Receiving . DownloadsPath , dir . Name )
@ -586,15 +641,6 @@ func (node *Node) Start() {
panic ( err )
}
// notify the node that we`re ready to transportation. No need
// for encryption because the body is nil
err = protocol . SendPacket ( node . netInfo . Conn , protocol . Packet {
Header : protocol . HeaderReady ,
} )
if err != nil {
panic ( err )
}
} else {
// no
@ -608,20 +654,23 @@ func (node *Node) Start() {
}
node . mutex . Lock ( )
node . state . S topped = true
node . stopped = true
node . mutex . Unlock ( )
}
} ( )
case protocol . HeaderFile :
// add file to the accepted files;
file , err := protocol . DecodeFilePacket ( incomingPacket )
if err != nil {
panic ( err )
}
if file . RelativeParentPath == "" {
if node . verboseOutput {
fmt . Printf ( "\n[File] Received info on \"%s\" - %d bytes" , file . Name , file . Size )
}
if strings . TrimSpace ( file . RelativeParentPath ) == "" {
// does not have a parent dir
file . Path = filepath . Join ( node . transferInfo . Receiving . DownloadsPath , file . Name )
} else {
@ -641,17 +690,18 @@ func (node *Node) Start() {
// check if it is the exact file
existingFileHandler , err := os . Open ( file . Path )
if err != nil {
os . Remove ( file . Path )
panic ( err )
}
existingFileChecksum , _ := checksum . GetPartialCheckSum ( existingFileHandler )
existingFileChecksum , err := checksum . GetPartialCheckSum ( existingFileHandler )
if err != nil {
panic ( err )
}
if existingFileChecksum == file . Checksum {
// it`s the exact same file. No need to receive it again
// notify the other node
fmt . Printf ( "Already have \"%s\". Skipping...\n\n" , file . Name )
alreadyHavePacketBodyBuffer := new ( bytes . Buffer )
binary . Write ( alreadyHavePacketBodyBuffer , binary . BigEndian , file . ID )
@ -660,18 +710,36 @@ func (node *Node) Start() {
Body : alreadyHavePacketBodyBuffer . Bytes ( ) ,
}
if node . netInfo . EncryptionKey != nil {
encryptedBody , err := encryption . Encrypt ( node . netInfo . EncryptionKey , alreadyHavePacket . Body )
if err != nil {
panic ( err )
}
alreadyHavePacket . Body = encryptedBody
}
protocol . SendPacket ( node . netInfo . Conn , alreadyHavePacket )
node . transferInfo . Receiving . ReceivedBytes += file . Size
if node . verboseOutput {
fmt . Printf ( "\n[File] already have \"%s\"" , file . Name )
}
} else {
// not the same file. Remove it and await new bytes
os . Remove ( file . Path )
node . mutex . Lock ( )
node . transferInfo . Receiving . AcceptedFiles = append ( node . transferInfo . Receiving . AcceptedFiles , file )
node . mutex . Unlock ( )
err = protocol . SendPacket ( node . netInfo . Conn , protocol . Packet {
Header : protocol . HeaderReady ,
} )
if err != nil {
panic ( err )
}
}
existingFileHandler . Close ( )
@ -681,6 +749,13 @@ func (node *Node) Start() {
node . mutex . Lock ( )
node . transferInfo . Receiving . AcceptedFiles = append ( node . transferInfo . Receiving . AcceptedFiles , file )
node . mutex . Unlock ( )
err = protocol . SendPacket ( node . netInfo . Conn , protocol . Packet {
Header : protocol . HeaderReady ,
} )
if err != nil {
panic ( err )
}
}
case protocol . HeaderFileBytes :
@ -699,11 +774,12 @@ func (node *Node) Start() {
// accepted
// append provided bytes to the file
if acceptedFile . Handler == nil {
err = acceptedFile . Open ( )
if err != nil {
panic ( err )
}
}
fileBytes := fileBytesBuffer . Bytes ( )
@ -712,26 +788,17 @@ func (node *Node) Start() {
panic ( err )
}
acceptedFile . SentBytes += uint64 ( wrote )
err = acceptedFile . Close ( )
if err != nil {
panic ( err )
}
node . transferInfo . Receiving . ReceivedBytes += uint64 ( wrote )
}
}
// notify the other node that this one is ready
err = protocol . SendPacket ( node . netInfo . Conn , protocol . Packet {
readyPacket := protocol . Packet {
Header : protocol . HeaderReady ,
} )
if err != nil {
panic ( err )
}
protocol . SendPacket ( node . netInfo . Conn , readyPacket )
case protocol . HeaderEndfile :
// one of the files has been received completely,
// compare checksums and check if it is the last
// file in the transfer
// one of the files has been received completely
fileIDReader := bytes . NewReader ( incomingPacket . Body )
var fileID uint64
@ -744,10 +811,16 @@ func (node *Node) Start() {
if acceptedFile . ID == fileID {
// accepted
if node . verboseOutput {
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 : ] ... )
@ -758,13 +831,14 @@ func (node *Node) Start() {
panic ( err )
}
fmt . Printf ( "\n| Checking hashes for file \"%s\"\n" , acceptedFile . Name )
if realChecksum != acceptedFile . Checksum {
fmt . Printf ( "| %s --- %s file is corrupted\n" , realChecksum , acceptedFile . Checksum )
if node . verboseOutput {
fmt . Printf ( "\n[ERROR] \"%s\" is corrupted" , acceptedFile . Name )
}
acceptedFile . Close ( )
break
} else {
fmt . Printf ( "| %s --- %s\n" , realChecksum , acceptedFile . Checksum )
acceptedFile . Close ( )
break
}
@ -790,21 +864,64 @@ func (node *Node) Start() {
node . netInfo . EncryptionKey = encrKey
fmt . Printf ( "Got an encryption key: %s\n" , encrKey )
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 . state . Stopped = true
node . stopped = true
node . mutex . Unlock ( )
case protocol . HeaderDisconnecting :
node . mutex . Lock ( )
node . state . Stopped = true
node . stopped = true
node . mutex . Unlock ( )
fmt . Printf ( "%s disconnected\n" , node . netInfo . Conn . RemoteAddr ( ) )
fmt . Printf ( "\n %s disconnected" , node . netInfo . Conn . RemoteAddr ( ) )
}
}
}
// Starts the node in either sending or receiving state and performs the transfer
func ( node * Node ) Start ( ) {
switch node . isSending {
case true :
node . send ( )
case false :
node . receive ( )
}
}