@ -1,6 +1,6 @@
/ *
/ *
ftu - file transferring utility .
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
This file is a part of ftu
@ -32,19 +32,13 @@ import (
"fmt"
"fmt"
"gith ub.com/U nbewohnte/ftu/addr"
"unbewohnte/ftu/addr"
"gith ub.com/U nbewohnte/ftu/checksum"
"unbewohnte/ftu/checksum"
"gith ub.com/U nbewohnte/ftu/encryption"
"unbewohnte/ftu/encryption"
"gith ub.com/U nbewohnte/ftu/fsys"
"unbewohnte/ftu/fsys"
"gith ub.com/U nbewohnte/ftu/protocol"
"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
// netInfowork specific settings
type netInfo struct {
type netInfo struct {
ConnAddr string // address to connect to. Does not include port
ConnAddr string // address to connect to. Does not include port
@ -59,14 +53,22 @@ type sending struct {
IsDirectory bool // is ServingPath a directory
IsDirectory bool // is ServingPath a directory
Recursive bool // recursively send directory
Recursive bool // recursively send directory
CanSendBytes bool // is the other node ready to receive another piece
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
FilesToSend [ ] * fsys . File
SymlinksToSend [ ] * fsys . Symlink
CurrentFileID uint64 // an id of a file that is currently being transported
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
// Receiving-side node information
type receiving struct {
type receiving struct {
AcceptedFiles [ ] * fsys . File // files that`ve been accepted to be received
AcceptedFiles [ ] * fsys . File // files that`ve been accepted to be received
DownloadsPath string // where to download
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
// Both sending-side and receiving-side information
@ -81,8 +83,8 @@ type Node struct {
mutex * sync . Mutex
mutex * sync . Mutex
packetPipe chan * protocol . Packet // a way to receive incoming packets from another goroutine
packetPipe chan * protocol . Packet // a way to receive incoming packets from another goroutine
isSending bool // sending or a receiving node
isSending bool // sending or a receiving node
stopped bool // the way to exit the mainloop in case of an external error or a successful end of a transfer
netInfo * netInfo
netInfo * netInfo
state * nodeInnerstates
transferInfo * transferInfo
transferInfo * transferInfo
}
}
@ -91,7 +93,7 @@ func NewNode(options *NodeOptions) (*Node, error) {
var isDir bool
var isDir bool
if options . IsSending {
if options . IsSending {
// sending node preparation
// sending node preparation
sendingPathStats , err := os . Stat ( options . Serv erSide . ServingPath )
sendingPathStats , err := os . Stat ( options . Send erSide . ServingPath )
if err != nil {
if err != nil {
return nil , err
return nil , err
}
}
@ -106,12 +108,12 @@ func NewNode(options *NodeOptions) (*Node, error) {
} else {
} else {
// receiving node preparation
// receiving node preparation
var err error
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 {
if err != nil {
return nil , err
return nil , err
}
}
err = os . MkdirAll ( options . Client Side. DownloadsFolderPath , os . ModePerm )
err = os . MkdirAll ( options . Receiver Side. DownloadsFolderPath , os . ModePerm )
if err != nil {
if err != nil {
return nil , err
return nil , err
}
}
@ -125,23 +127,24 @@ func NewNode(options *NodeOptions) (*Node, error) {
isSending : options . IsSending ,
isSending : options . IsSending ,
netInfo : & netInfo {
netInfo : & netInfo {
Port : options . WorkingPort ,
Port : options . WorkingPort ,
ConnAddr : options . Client Side. ConnectionAddr ,
ConnAddr : options . Receiver Side. ConnectionAddr ,
EncryptionKey : nil ,
EncryptionKey : nil ,
Conn : nil ,
Conn : nil ,
} ,
} ,
state : & nodeInnerstates {
stopped : false ,
AllowedToTransfer : false ,
Stopped : false ,
} ,
transferInfo : & transferInfo {
transferInfo : & transferInfo {
Sending : & sending {
Sending : & sending {
ServingPath : options . Serv erSide . ServingPath ,
ServingPath : options . SenderSide . ServingPath ,
Recursive : options . Serv erSide . Recursive ,
Recursive : options . Send erSide . Recursive ,
IsDirectory : isDir ,
IsDirectory : isDir ,
TotalTransferSize : 0 ,
SentBytes : 0 ,
} ,
} ,
Receiving : & receiving {
Receiving : & receiving {
AcceptedFiles : nil ,
AcceptedFiles : nil ,
DownloadsPath : options . ClientSide . DownloadsFolderPath ,
DownloadsPath : options . ReceiverSide . DownloadsFolderPath ,
ReceivedBytes : 0 ,
TotalDownloadSize : 0 ,
} ,
} ,
} ,
} ,
}
}
@ -184,7 +187,7 @@ func (node *Node) disconnect() error {
return err
return err
}
}
node . state . St opped = true
node . stopped = true
}
}
return nil
return nil
@ -216,24 +219,25 @@ func (node *Node) printTransferInfo(delay time.Duration) error {
switch node . isSending {
switch node . isSending {
case true :
case true :
if ! node . state . AllowedToTransfer {
if ! node . transferInfo . Sending . AllowedToTransfer {
// do not print if the transfer has not been accepted yet
break
break
}
}
fmt . Printf ( "\r| files(s) left to send: %4d" , len ( node . transferInfo . Sending . FilesToSend ) )
fmt . Printf ( "\r| (%.2f/%.2f MB)" ,
float32 ( node . transferInfo . Sending . SentBytes ) / 1024 / 1024 ,
float32 ( node . transferInfo . Sending . TotalTransferSize ) / 1024 / 1024 ,
)
case false :
case false :
if len ( node . transferInfo . Receiving . AcceptedFiles ) <= 0 {
fmt . Printf ( "\r| (%.2f/%.2f MB)" ,
break
float32 ( node . transferInfo . Receiving . ReceivedBytes ) / 1024 / 1024 ,
}
float32 ( node . transferInfo . Receiving . TotalDownloadSize ) / 1024 / 1024 ,
fmt . Printf ( "\r| file(s) left to receive: %4d" , len ( node . transferInfo . Receiving . AcceptedFiles ) )
)
}
}
return nil
return nil
}
}
// Starts the node in either sending or receiving state and performs the transfer
func ( node * Node ) send ( ) {
func ( node * Node ) Start ( ) {
switch node . isSending {
case true :
// SENDER NODE
// SENDER NODE
localIP , err := addr . GetLocal ( )
localIP , err := addr . GetLocal ( )
@ -242,40 +246,44 @@ func (node *Node) Start() {
}
}
// retrieve information about the file|directory
// retrieve information about the file|directory
var fileToSend * fsys . File
var FILETOSEND * fsys . File
var dirToSend * fsys . Directory
var DIRTOSEND * fsys . Directory
switch node . transferInfo . Sending . IsDirectory {
switch node . transferInfo . Sending . IsDirectory {
case true :
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 {
if err != nil {
panic ( err )
panic ( err )
}
}
case false :
case false :
fileToSend , err = fsys . GetFile ( node . transferInfo . Sending . ServingPath )
FILETOSEND , err = fsys . GetFile ( node . transferInfo . Sending . ServingPath )
if err != nil {
if err != nil {
panic ( err )
panic ( err )
}
}
}
}
if dirToSend != nil {
if DIRTOSEND != nil {
size := float32 ( dirToSend . Size ) / 1024 / 1024
node . transferInfo . Sending . TotalTransferSize = DIRTOSEND . Size
displaySize := float32 ( DIRTOSEND . Size ) / 1024 / 1024
sizeLevel := "MiB"
sizeLevel := "MiB"
if size >= 1024 {
if di splayS ize >= 1024 {
// GiB
// GiB
size = size / 1024
di splayS ize = di splayS ize / 1024
sizeLevel = "GiB"
sizeLevel = "GiB"
}
}
fmt . Printf ( "\nSending \"%s\" (%.3f %s) locally on %s:%d" , 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 {
} else {
size := float32 ( fileToSend . Size ) / 1024 / 1024
node . transferInfo . Sending . TotalTransferSize = FILETOSEND . Size
displaySize := float32 ( FILETOSEND . Size ) / 1024 / 1024
sizeLevel := "MiB"
sizeLevel := "MiB"
if size >= 1024 {
if di splayS ize >= 1024 {
// GiB
// GiB
size = size / 1024
di splayS ize = di splayS ize / 1024
sizeLevel = "GiB"
sizeLevel = "GiB"
}
}
fmt . Printf ( "\nSending \"%s\" (%.3f %s) locally on %s:%d and remotely" , 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 )
}
}
@ -299,21 +307,25 @@ func (node *Node) Start() {
go protocol . ReceivePackets ( node . netInfo . Conn , node . packetPipe )
go protocol . ReceivePackets ( node . netInfo . Conn , node . packetPipe )
// send info about file/directory
// 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
// mainloop
for {
for {
if node . state . S topped {
if node . stopped {
fmt . Printf ( "\n" )
fmt . Printf ( "\n" )
node . disconnect ( )
node . disconnect ( )
break
break
}
}
if ! node . verboseOutput {
go node . printTransferInfo ( time . Second )
}
// receive incoming packets and decrypt them if necessary
// receive incoming packets and decrypt them if necessary
incomingPacket , ok := <- node . packetPipe
incomingPacket , ok := <- node . packetPipe
if ! ok {
if ! ok {
fmt . Printf ( "\nThe connection has been closed unexpectedly\n" )
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
// if encryption key is set - decrypt packet on the spot
@ -333,107 +345,46 @@ func (node *Node) Start() {
case protocol . HeaderAccept :
case protocol . HeaderAccept :
// the receiving node has accepted the transfer
// the receiving node has accepted the transfer
node . state . AllowedToTransfer = true
node . transferInfo . Sending . AllowedToTransfer = true
fmt . Printf ( "\nTransfer allowed. Sending..." )
// notify it about all the files that are going to be sent
// prepare files to send
switch node . transferInfo . Sending . IsDirectory {
switch node . transferInfo . Sending . IsDirectory {
case true :
case true :
// send file packets for the files in the directory
// 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 {
if err != nil {
panic ( err )
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 {
for counter , file := range filesToSend {
// assign ID and add it to the node sendlist
// assign ID and add it to the node sendlist
file . ID = uint64 ( counter )
file . ID = uint64 ( counter )
node . transferInfo . Sending . FilesToSend = append ( node . transferInfo . Sending . FilesToSend , file )
node . transferInfo . Sending . FilesToSend = append ( node . transferInfo . Sending . FilesToSend , file )
}
// set current file id to the first file
// set current file id to the first file
node . transferInfo . Sending . CurrentFileID = 0
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 )
}
if node . verboseOutput {
fmt . Printf ( "\n[File] Sent filepacket for \"%s\"" , file . Name )
}
}
filesInfoDonePacket := protocol . Packet {
Header : protocol . HeaderFilesInfoDone ,
}
protocol . SendPacket ( node . netInfo . Conn , filesInfoDonePacket )
if node . verboseOutput {
fmt . Printf ( "\n[File] Done sending filepackets" )
}
case false :
case false :
// send a filepacket of a single file
FILETOSEND . ID = 0
fileToSend . ID = 0
node . transferInfo . Sending . FilesToSend = append ( node . transferInfo . Sending . FilesToSend , FILETOSEND )
node . transferInfo . Sending . FilesToSend = append ( node . transferInfo . Sending . FilesToSend , fileToSend )
// set current file index to the first and only file
// set current file index to the first and only file
node . transferInfo . Sending . CurrentFileID = 0
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 )
}
if node . verboseOutput {
fmt . Printf ( "\n[File] Sent filepacket for \"%s\"" , fileToSend . Name )
}
if node . verboseOutput {
fmt . Printf ( "\n[File] Done sending filepackets" )
}
}
}
fmt . Printf ( "\n" )
case protocol . HeaderReject :
case protocol . HeaderReject :
node . state . S topped = true
node . stopped = true
fmt . Printf ( "\nTransfer rejected. Disconnecting..." )
fmt . Printf ( "\nTransfer rejected. Disconnecting..." )
case protocol . HeaderDisconnecting :
case protocol . HeaderDisconnecting :
node . state . S topped = true
node . stopped = true
fmt . Printf ( "\n%s disconnected" , node . netInfo . Conn . RemoteAddr ( ) )
fmt . Printf ( "\n%s disconnected" , node . netInfo . Conn . RemoteAddr ( ) )
case protocol . HeaderAlreadyHave :
case protocol . HeaderAlreadyHave :
@ -450,35 +401,74 @@ func (node *Node) Start() {
node . transferInfo . Sending . CurrentFileID ++
node . transferInfo . Sending . CurrentFileID ++
node . transferInfo . Sending . SentBytes += fileToSend . Size
node . transferInfo . Sending . InTransfer = false
if node . verboseOutput {
if node . verboseOutput {
fmt . Printf ( "\n[File] receiver already has \"%s\"" , fileToSend . Name )
fmt . Printf ( "\n[File] receiver already has \"%s\"" , fileToSend . Name )
}
}
}
}
}
}
}
if ! node . verboseOutput {
go node . printTransferInfo ( time . Second )
}
}
// Transfer section
// 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
// if there`s nothing else to send - create and send DONE packet
protocol . SendPacket ( node . netInfo . Conn , protocol . Packet {
protocol . SendPacket ( node . netInfo . Conn , protocol . Packet {
Header : protocol . HeaderDone ,
Header : protocol . HeaderDone ,
} )
} )
fmt . Printf ( "\nTransfer ended successfully" )
node . stopped = true
node . state . 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 )
}
}
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
continue
}
}
// if allowed to transfer and the other node is ready to receive packets - send one piece
// if allowed to transfer and the other node is ready to receive packets - send one piece
// and wait for it to be ready again
// 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
// sending a piece of a single file
// determine an index of a file with current ID
// determine an index of a file with current ID
@ -490,7 +480,8 @@ 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 {
switch err {
case protocol . ErrorSentAll :
case protocol . ErrorSentAll :
// the file has been sent fully
// the file has been sent fully
@ -519,30 +510,33 @@ func (node *Node) Start() {
protocol . SendPacket ( node . netInfo . Conn , endFilePacket )
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 : ] ... )
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 . CurrentFileID ++
node . transferInfo . Sending . InTransfer = false
case nil :
case nil :
node . transferInfo . Sending . CanSendBytes = false
node . transferInfo . Sending . CanSendBytes = false
default :
default :
node . state . S topped = true
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 )
panic ( err )
}
}
}
}
}
}
}
case false :
func ( node * Node ) receive ( ) {
// RECEIVER NODE
// RECEIVER NODE
// connect to the sending node
// connect to the sending node
err := node . connect ( )
err := node . connect ( )
if err != nil {
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 )
os . Exit ( - 1 )
}
}
@ -552,7 +546,7 @@ func (node *Node) Start() {
// mainloop
// mainloop
for {
for {
node . mutex . Lock ( )
node . mutex . Lock ( )
stopped := node . state . S topped
stopped := node . stopped
node . mutex . Unlock ( )
node . mutex . Unlock ( )
if stopped {
if stopped {
@ -561,10 +555,14 @@ func (node *Node) Start() {
break
break
}
}
if ! node . verboseOutput && node . transferInfo . Receiving . ReceivedBytes != 0 {
go node . printTransferInfo ( time . Second )
}
// receive incoming packets and decrypt them if necessary
// receive incoming packets and decrypt them if necessary
incomingPacket , ok := <- node . packetPipe
incomingPacket , ok := <- node . packetPipe
if ! ok {
if ! ok {
fmt . Printf ( "\nThe connection has been closed unexpectedly\n" )
fmt . Printf ( "\nC onnection has been closed unexpectedly\n" )
os . Exit ( - 1 )
os . Exit ( - 1 )
}
}
@ -588,6 +586,8 @@ func (node *Node) Start() {
}
}
if file != nil {
if file != nil {
node . transferInfo . Receiving . TotalDownloadSize = file . Size
size := float32 ( file . Size ) / 1024 / 1024
size := float32 ( file . Size ) / 1024 / 1024
sizeLevel := "MiB"
sizeLevel := "MiB"
if size >= 1024 {
if size >= 1024 {
@ -596,7 +596,10 @@ func (node *Node) Start() {
sizeLevel = "GiB"
sizeLevel = "GiB"
}
}
fmt . Printf ( "\n| Filename: %s\n| Size: %.3f %s\n| Checksum: %s\n" , file . Name , size , sizeLevel , file . Checksum )
fmt . Printf ( "\n| Filename: %s\n| Size: %.3f %s\n| Checksum: %s\n" , file . Name , size , sizeLevel , file . Checksum )
} else if dir != nil {
} else if dir != nil {
node . transferInfo . Receiving . TotalDownloadSize = dir . Size
size := float32 ( dir . Size ) / 1024 / 1024
size := float32 ( dir . Size ) / 1024 / 1024
sizeLevel := "MiB"
sizeLevel := "MiB"
if size >= 1024 {
if size >= 1024 {
@ -620,7 +623,7 @@ func (node *Node) Start() {
err = os . MkdirAll ( filepath . Join ( node . transferInfo . Receiving . DownloadsPath , dir . Name ) , os . ModePerm )
err = os . MkdirAll ( filepath . Join ( node . transferInfo . Receiving . DownloadsPath , dir . Name ) , os . ModePerm )
if err != nil {
if err != nil {
// well, just download all files in the default downloads folder then
// well, just download all files in the default downloads folder then
fmt . Printf ( "\n[ERROR] could not create a directory" )
fmt . Printf ( "\n[ERROR] could not create a directory, downloading directly to the specified location " )
} else {
} else {
// also download everything in a newly created directory
// also download everything in a newly created directory
node . transferInfo . Receiving . DownloadsPath = filepath . Join ( node . transferInfo . Receiving . DownloadsPath , dir . Name )
node . transferInfo . Receiving . DownloadsPath = filepath . Join ( node . transferInfo . Receiving . DownloadsPath , dir . Name )
@ -651,14 +654,13 @@ func (node *Node) Start() {
}
}
node . mutex . Lock ( )
node . mutex . Lock ( )
node . state . S topped = true
node . stopped = true
node . mutex . Unlock ( )
node . mutex . Unlock ( )
}
}
} ( )
} ( )
case protocol . HeaderFile :
case protocol . HeaderFile :
// add file to the accepted files;
// add file to the accepted files;
file , err := protocol . DecodeFilePacket ( incomingPacket )
file , err := protocol . DecodeFilePacket ( incomingPacket )
if err != nil {
if err != nil {
panic ( err )
panic ( err )
@ -718,6 +720,8 @@ func (node *Node) Start() {
protocol . SendPacket ( node . netInfo . Conn , alreadyHavePacket )
protocol . SendPacket ( node . netInfo . Conn , alreadyHavePacket )
node . transferInfo . Receiving . ReceivedBytes += file . Size
if node . verboseOutput {
if node . verboseOutput {
fmt . Printf ( "\n[File] already have \"%s\"" , file . Name )
fmt . Printf ( "\n[File] already have \"%s\"" , file . Name )
}
}
@ -725,6 +729,17 @@ func (node *Node) Start() {
} else {
} else {
// not the same file. Remove it and await new bytes
// not the same file. Remove it and await new bytes
os . Remove ( file . Path )
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 ( )
existingFileHandler . Close ( )
@ -734,6 +749,13 @@ func (node *Node) Start() {
node . mutex . Lock ( )
node . mutex . Lock ( )
node . transferInfo . Receiving . AcceptedFiles = append ( node . transferInfo . Receiving . AcceptedFiles , file )
node . transferInfo . Receiving . AcceptedFiles = append ( node . transferInfo . Receiving . AcceptedFiles , file )
node . mutex . Unlock ( )
node . mutex . Unlock ( )
err = protocol . SendPacket ( node . netInfo . Conn , protocol . Packet {
Header : protocol . HeaderReady ,
} )
if err != nil {
panic ( err )
}
}
}
case protocol . HeaderFileBytes :
case protocol . HeaderFileBytes :
@ -752,11 +774,12 @@ func (node *Node) Start() {
// accepted
// accepted
// append provided bytes to the file
// append provided bytes to the file
if acceptedFile . Handler == nil {
err = acceptedFile . Open ( )
err = acceptedFile . Open ( )
if err != nil {
if err != nil {
panic ( err )
panic ( err )
}
}
}
fileBytes := fileBytesBuffer . Bytes ( )
fileBytes := fileBytesBuffer . Bytes ( )
@ -765,11 +788,7 @@ func (node *Node) Start() {
panic ( err )
panic ( err )
}
}
acceptedFile . SentBytes += uint64 ( wrote )
acceptedFile . SentBytes += uint64 ( wrote )
node . transferInfo . Receiving . ReceivedBytes += uint64 ( wrote )
err = acceptedFile . Close ( )
if err != nil {
panic ( err )
}
}
}
}
}
@ -778,21 +797,8 @@ func (node *Node) Start() {
}
}
protocol . SendPacket ( node . netInfo . Conn , readyPacket )
protocol . SendPacket ( node . netInfo . Conn , readyPacket )
case protocol . HeaderFilesInfoDone :
// have all information about the files
// notify the other node that this one is ready
err = protocol . SendPacket ( node . netInfo . Conn , protocol . Packet {
Header : protocol . HeaderReady ,
} )
if err != nil {
panic ( err )
}
case protocol . HeaderEndfile :
case protocol . HeaderEndfile :
// one of the files has been received completely,
// one of the files has been received completely
// compare checksums and check if it is the last
// file in the transfer
fileIDReader := bytes . NewReader ( incomingPacket . Body )
fileIDReader := bytes . NewReader ( incomingPacket . Body )
var fileID uint64
var fileID uint64
@ -809,10 +815,12 @@ func (node *Node) Start() {
fmt . Printf ( "\n[File] fully received \"%s\" -- %d bytes" , acceptedFile . Name , acceptedFile . Size )
fmt . Printf ( "\n[File] fully received \"%s\" -- %d bytes" , acceptedFile . Name , acceptedFile . Size )
}
}
if acceptedFile . Handler == nil {
err = acceptedFile . Open ( )
err = acceptedFile . Open ( )
if err != nil {
if err != nil {
panic ( err )
panic ( err )
}
}
}
// remove this file from the pool
// remove this file from the pool
node . transferInfo . Receiving . AcceptedFiles = append ( node . transferInfo . Receiving . AcceptedFiles [ : index ] , node . transferInfo . Receiving . AcceptedFiles [ index + 1 : ] ... )
node . transferInfo . Receiving . AcceptedFiles = append ( node . transferInfo . Receiving . AcceptedFiles [ : index ] , node . transferInfo . Receiving . AcceptedFiles [ index + 1 : ] ... )
@ -824,7 +832,10 @@ func (node *Node) Start() {
}
}
if realChecksum != acceptedFile . Checksum {
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 ( )
acceptedFile . Close ( )
break
break
} else {
} else {
@ -855,22 +866,62 @@ func (node *Node) Start() {
fmt . Printf ( "\nGot an encryption key: %s" , 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 :
case protocol . HeaderDone :
node . mutex . Lock ( )
node . mutex . Lock ( )
node . state . Stopped = true
node . stopped = true
node . mutex . Unlock ( )
node . mutex . Unlock ( )
case protocol . HeaderDisconnecting :
case protocol . HeaderDisconnecting :
node . mutex . Lock ( )
node . mutex . Lock ( )
node . state . Stopped = true
node . stopped = true
node . mutex . Unlock ( )
node . mutex . Unlock ( )
fmt . Printf ( "\n%s disconnected" , node . netInfo . Conn . RemoteAddr ( ) )
fmt . Printf ( "\n%s disconnected" , node . netInfo . Conn . RemoteAddr ( ) )
}
}
if ! node . verboseOutput {
go node . printTransferInfo ( time . Second )
}
}
}
}
// 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 ( )
}
}
}
}