From 075c342dbd53b48d317358e88eff6a8dd2d9edf5 Mon Sep 17 00:00:00 2001 From: Unbewohnte Date: Wed, 10 Nov 2021 13:09:29 +0300 Subject: [PATCH] [protocol] added relative path logic to the FILE packet; [fsys] file relative path to its parent directory; [node&&FTU] CAN SEND DIRECTORIES RECURSIVELY --- src/fsys/dir.go | 25 ++--- src/fsys/dir_test.go | 8 ++ src/fsys/file.go | 25 +++-- src/node/node.go | 165 ++++++++++++++++++-------------- src/protocol/headers.go | 8 +- src/protocol/packetConstruct.go | 7 +- src/protocol/packetDecode.go | 27 ++++-- src/protocol/protocol_test.go | 5 +- 8 files changed, 163 insertions(+), 107 deletions(-) diff --git a/src/fsys/dir.go b/src/fsys/dir.go index fd5c9fe..78e7549 100644 --- a/src/fsys/dir.go +++ b/src/fsys/dir.go @@ -8,12 +8,12 @@ import ( // A struct that represents the main information about a directory type Directory struct { - Name string - Path string - ParentPath string - Size uint64 - Files []*File - Directories []*Directory + Name string + 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 + Files []*File + Directories []*Directory } var ErrorNotDirectory error = fmt.Errorf("not a directory") @@ -37,7 +37,6 @@ func GetDir(path string, recursive bool) (*Directory, error) { directory := Directory{ Name: stats.Name(), Path: absPath, - ParentPath: filepath.Dir(absPath), Directories: nil, Files: nil, } @@ -66,9 +65,13 @@ func GetDir(path string, recursive bool) (*Directory, error) { return nil, err } - innerDirs = append(innerDirs, innerDir) + for _, file := range innerDir.Files { + file.RelativeParentPath = filepath.Join(directory.Name, innerDir.Name, file.Name) + } directory.Size += innerDir.Size + + innerDirs = append(innerDirs, innerDir) } // if not - skip the directory and only work with the files @@ -80,9 +83,11 @@ func GetDir(path string, recursive bool) (*Directory, error) { return nil, err } - innerFiles = append(innerFiles, innerFile) + innerFile.RelativeParentPath = filepath.Join(directory.Name, innerFile.Name) directory.Size += innerFile.Size + + innerFiles = append(innerFiles, innerFile) } } @@ -92,8 +97,6 @@ func GetDir(path string, recursive bool) (*Directory, error) { return &directory, nil } -var FILES []*File - // Returns every file in that directory func (dir *Directory) GetAllFiles(recursively bool) []*File { var files []*File = dir.Files diff --git a/src/fsys/dir_test.go b/src/fsys/dir_test.go index 660caac..7e41155 100644 --- a/src/fsys/dir_test.go +++ b/src/fsys/dir_test.go @@ -29,6 +29,14 @@ func Test_GetDirRecursive(t *testing.T) { t.Fatalf("inner dir cannot have a bigger size (%d B) than its parent`s total size (%d B)", innerDir.Size, dir.Size) } } + + // t.Errorf("[initialdir] %+v", dir.Files[0]) + // for _, dir := range dir.Directories { + // for countf, file := range dir.Files { + // t.Errorf("[%d] %+v\n", countf, file) + // } + // } + } func Test_GetFiles(t *testing.T) { diff --git a/src/fsys/file.go b/src/fsys/file.go index 557aea2..d2ab02c 100644 --- a/src/fsys/file.go +++ b/src/fsys/file.go @@ -10,14 +10,14 @@ import ( // A struct that represents the necessary file information for transportation through node type File struct { - ID uint64 // Set manually - Name string - Path string - ParentPath string - Size uint64 - Checksum string - Handler *os.File // Set when .Open() is called - SentBytes uint64 // Set manually during transportation + ID uint64 // Set manually + Name string + Path string + RelativeParentPath string // Relative path to the file, where the highest directory in the hierarchy is the upmost parent dir. Set manually + Size uint64 + Checksum string + Handler *os.File // Set when .Open() is called + SentBytes uint64 // Set manually during transportation } var ErrorNotFile error = fmt.Errorf("not a file") @@ -43,11 +43,10 @@ func GetFile(path string) (*File, error) { } file := File{ - Name: stats.Name(), - Path: absPath, - ParentPath: filepath.Dir(absPath), - Size: uint64(stats.Size()), - Handler: nil, + Name: stats.Name(), + Path: absPath, + Size: uint64(stats.Size()), + Handler: nil, } // get checksum diff --git a/src/node/node.go b/src/node/node.go index 1df5f48..013e4fa 100644 --- a/src/node/node.go +++ b/src/node/node.go @@ -35,12 +35,12 @@ type netInfoInfo struct { // Sending-side node information type sending struct { - ServingPath string // path to the thing that will be sent - IsDirectory bool // is ServingPath a directory - Recursive bool // recursively send directory - CanSendBytes bool // is the other node ready to receive another piece - FilesToSend []*fsys.File - CurrentFileID uint64 // an id of a file that is currently being transported + ServingPath string // path to the thing that will be sent + IsDirectory bool // is ServingPath a directory + Recursive bool // recursively send directory + CanSendBytes bool // is the other node ready to receive another piece + FilesToSend []*fsys.File + CurrentFileIndex uint64 // an id of a file that is currently being transported } // Receiving-side node information @@ -209,9 +209,24 @@ func (node *Node) Start() { } if dirToSend != nil { - fmt.Printf("Sending \"%s\" (%.2f MB) locally on %s:%d\n", dirToSend.Name, float32(dirToSend.Size)/1024/1024, localIP, node.netInfo.Port) + size := float32(dirToSend.Size) / 1024 / 1024 + sizeLevel := "MiB" + if size >= 1024 { + // GiB + size = size / 1024 + sizeLevel = "GiB" + } + + fmt.Printf("Sending \"%s\" (%.3f %s) locally on %s:%d\n", dirToSend.Name, size, sizeLevel, localIP, node.netInfo.Port) } else { - fmt.Printf("Sending \"%s\" (%.2f MB) locally on %s:%d\n", fileToSend.Name, float32(fileToSend.Size)/1024/1024, localIP, node.netInfo.Port) + size := float32(dirToSend.Size) / 1024 / 1024 + sizeLevel := "MiB" + if size >= 1024 { + // GiB + size = size / 1024 + sizeLevel = "GiB" + } + fmt.Printf("Sending \"%s\" (%.3f %s) locally on %s:%d\n", fileToSend.Name, size, sizeLevel, localIP, node.netInfo.Port) } @@ -248,7 +263,7 @@ func (node *Node) Start() { incomingPacket, ok := <-node.packetPipe if !ok { fmt.Printf("The connection has been closed unexpectedly\n") - break + os.Exit(-1.) } // if encryption key is set - decrypt packet on the spot @@ -291,6 +306,9 @@ func (node *Node) Start() { 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.CurrentFileIndex = 0 + filePacket, err := protocol.CreateFilePacket(file) if err != nil { panic(err) @@ -311,14 +329,14 @@ func (node *Node) Start() { } } - // set current file id to the first file - node.transferInfo.Sending.CurrentFileID = 0 - case false: // send a filepacket of a single file 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.CurrentFileIndex = 0 + filePacket, err := protocol.CreateFilePacket(node.transferInfo.Sending.FilesToSend[0]) if err != nil { panic(err) @@ -338,14 +356,12 @@ func (node *Node) Start() { panic(err) } - // set current file id to the first and only file - node.transferInfo.Sending.CurrentFileID = 0 } case protocol.HeaderReject: node.state.Stopped = true - fmt.Printf("Transfer rejected. Disconnecting...") + fmt.Printf("Transfer rejected. Disconnecting...\n") case protocol.HeaderDisconnecting: node.state.Stopped = true @@ -354,30 +370,30 @@ func (node *Node) Start() { // Transfer section - if len(node.transferInfo.Sending.FilesToSend) == 0 { - // 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.state.Stopped = true - } - // 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 len(node.transferInfo.Sending.FilesToSend) == 0 { + // 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.state.Stopped = true + } + // sending a piece of a single file - currentFileID := node.transferInfo.Sending.CurrentFileID + currentFileIndex := node.transferInfo.Sending.CurrentFileIndex - err = protocol.SendPiece(node.transferInfo.Sending.FilesToSend[currentFileID], node.netInfo.Conn, node.netInfo.EncryptionKey) + err = protocol.SendPiece(node.transferInfo.Sending.FilesToSend[currentFileIndex], node.netInfo.Conn, node.netInfo.EncryptionKey) switch err { case protocol.ErrorSentAll: // the file has been sent fully fileIDBuff := new(bytes.Buffer) - err = binary.Write(fileIDBuff, binary.BigEndian, node.transferInfo.Sending.FilesToSend[currentFileID].ID) + err = binary.Write(fileIDBuff, binary.BigEndian, node.transferInfo.Sending.FilesToSend[currentFileIndex].ID) if err != nil { panic(err) } @@ -397,7 +413,7 @@ func (node *Node) Start() { protocol.SendPacket(node.netInfo.Conn, endFilePacket) // start sending the next file - if uint64(len(node.transferInfo.Sending.FilesToSend)) == (node.transferInfo.Sending.CurrentFileID + 1) { + if uint64(len(node.transferInfo.Sending.FilesToSend)) == (node.transferInfo.Sending.CurrentFileIndex + 1) { // all files has been sent node.state.Stopped = true @@ -409,7 +425,7 @@ func (node *Node) Start() { protocol.SendPacket(node.netInfo.Conn, donePacket) } else { - node.transferInfo.Sending.CurrentFileID++ + node.transferInfo.Sending.CurrentFileIndex++ } case nil: @@ -418,8 +434,8 @@ func (node *Node) Start() { default: node.state.Stopped = true - currentFileID := node.transferInfo.Sending.CurrentFileID - fmt.Printf("An error occured while sending a piece of \"%s\": %s\n", node.transferInfo.Sending.FilesToSend[currentFileID].Name, err) + CurrentFileIndex := node.transferInfo.Sending.CurrentFileIndex + fmt.Printf("An error occured while sending a piece of \"%s\": %s\n", node.transferInfo.Sending.FilesToSend[CurrentFileIndex].Name, err) panic(err) } @@ -454,7 +470,7 @@ func (node *Node) Start() { incomingPacket, ok := <-node.packetPipe if !ok { fmt.Printf("The connection has been closed unexpectedly\n") - break + os.Exit(-1) } // if encryption key is set - decrypt packet on the spot @@ -477,9 +493,23 @@ func (node *Node) Start() { } if file != nil { - fmt.Printf("\n| Filename: %s\n| Size: %.2f MB\n| Checksum: %s\n", file.Name, float32(file.Size)/1024/1024, file.Checksum) + size := float32(file.Size) / 1024 / 1024 + sizeLevel := "MiB" + if size >= 1024 { + // GiB + size = size / 1024 + 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 { - fmt.Printf("\n| Directory name: %s\n| Size: %.2f MB\n", dir.Name, float32(dir.Size)/1024/1024) + size := float32(dir.Size) / 1024 / 1024 + sizeLevel := "MiB" + if size >= 1024 { + // GiB + size = size / 1024 + sizeLevel = "GiB" + } + fmt.Printf("\n| Directory name: %s\n| Size: %.3f %s\n", dir.Name, size, sizeLevel) } var answer string @@ -490,18 +520,18 @@ func (node *Node) Start() { if strings.EqualFold(answer, "y") || answer == "" { // yes - // in case it`s a directory - create it now - if dir != nil { - 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") - } else { - // also download everything in a newly created directory - node.transferInfo.Receiving.DownloadsPath = filepath.Join(node.transferInfo.Receiving.DownloadsPath, dir.Name) - } + // // in case it`s a directory - create it now + // if dir != nil { + // 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") + // } else { + // // also download everything in a newly created directory + // node.transferInfo.Receiving.DownloadsPath = filepath.Join(node.transferInfo.Receiving.DownloadsPath, dir.Name) + // } - } + // } // send aceptance packet acceptancePacket := protocol.Packet{ @@ -547,44 +577,39 @@ func (node *Node) Start() { if err != nil { panic(err) } - fullFilePath := filepath.Join(node.transferInfo.Receiving.DownloadsPath, file.Name) + + file.Path, err = filepath.Abs(filepath.Join(node.transferInfo.Receiving.DownloadsPath, file.RelativeParentPath)) + if err != nil { + panic(err) + } + + // create all underlying directories right ahead + err = os.MkdirAll(filepath.Dir(file.Path), os.ModePerm) + if err != nil { + panic(err) + } // check if the file already exists; if yes - remove it and replace with a new one - _, err = os.Stat(fullFilePath) + _, err = os.Stat(file.Path) if err == nil { // exists // remove it - os.Remove(fullFilePath) + os.Remove(file.Path) } - file.Path = fullFilePath file.Open() node.mutex.Lock() node.transferInfo.Receiving.AcceptedFiles = append(node.transferInfo.Receiving.AcceptedFiles, file) node.mutex.Unlock() - case protocol.HeaderDirectory: - // (TODO) - - // directory - // dir, err := protocol.DecodeDirectoryPacket(incomingPacket) - // if err != nil { - // panic(err) - // } - - // // add a new directory to downloads path - // node.transferInfo.Receiving.DownloadsPath = filepath.Join(node.transferInfo.Receiving.DownloadsPath, dir.Name) - // err = os.MkdirAll(node.transferInfo.Receiving.DownloadsPath, os.ModePerm) - // if err != nil { - // panic(err) - // } - case protocol.HeaderFileBytes: // check if this file has been accepted to receive - fileIDReader := bytes.NewReader(incomingPacket.Body) + + fileBytesBuffer := bytes.NewBuffer(incomingPacket.Body) + var fileID uint64 - err := binary.Read(fileIDReader, binary.BigEndian, &fileID) + err := binary.Read(fileBytesBuffer, binary.BigEndian, &fileID) if err != nil { panic(err) } @@ -595,7 +620,7 @@ func (node *Node) Start() { // append provided bytes to the file - fileBytes := incomingPacket.Body[8:] + fileBytes := fileBytesBuffer.Bytes() _, err = acceptedFile.Handler.Write(fileBytes) if err != nil { panic(err) diff --git a/src/protocol/headers.go b/src/protocol/headers.go index 308aff4..82b34ff 100644 --- a/src/protocol/headers.go +++ b/src/protocol/headers.go @@ -65,7 +65,9 @@ const HeaderTransferOffer Header = "TRANSFEROFFER" // FILE. // Sent by sender, indicating that the file is going to be sent. // The body structure must follow such structure: -// FILE~(id in binary)(filename length in binary)(filename)(filesize)(checksum length in binary)(checksum) +// FILE~(id in binary)(filename length in binary)(filename)(filesize)(checksum length in binary)(checksum)(relative path to the upper directory size in binary if present)(relative path) +// relative path is not needed when the file is already in the root of the initial directory, but must be included when +// the whole directory is being sent recursively const HeaderFile Header = "FILE" // FILEBYTES. @@ -81,7 +83,7 @@ const HeaderFileBytes Header = "FILEBYTES" const HeaderEndfile Header = "ENDFILE" // DIRECTORY -// Sent by sender, indicates that a directory with current information -// is going to be sent. The structure of the body must follow the example: +// Sent by sender. Used in TRANSFEROFFER packet to tell the difference +// between a file and a directory. // ie: DIRECTORY~(dirname size in binary)(dirname)(dirsize) const HeaderDirectory Header = "DIRECTORY" diff --git a/src/protocol/packetConstruct.go b/src/protocol/packetConstruct.go index f2e1915..e1f0406 100644 --- a/src/protocol/packetConstruct.go +++ b/src/protocol/packetConstruct.go @@ -16,7 +16,7 @@ func CreateFilePacket(file *fsys.File) (*Packet, error) { } defer file.Handler.Close() - // FILE~(idInBinary)(filenameLengthInBinary)(filename)(filesize)(checksumLengthInBinary)(checksum) + //(id in binary)(filename length in binary)(filename)(filesize)(checksum length in binary)(checksum)(relative path to the upper directory size in binary if present)(relative path) filePacket := Packet{ Header: HeaderFile, @@ -39,6 +39,11 @@ func CreateFilePacket(file *fsys.File) (*Packet, error) { binary.Write(fPacketBodyBuff, binary.BigEndian, &checksumLen) fPacketBodyBuff.Write([]byte(file.Checksum)) + // relative path + relPathLen := uint64(len([]byte(file.RelativeParentPath))) + binary.Write(fPacketBodyBuff, binary.BigEndian, &relPathLen) + fPacketBodyBuff.Write([]byte(file.RelativeParentPath)) + filePacket.Body = fPacketBodyBuff.Bytes() // we do not check for packet size because there is no way that it`ll exceed current diff --git a/src/protocol/packetDecode.go b/src/protocol/packetDecode.go index 53feceb..2d22408 100644 --- a/src/protocol/packetDecode.go +++ b/src/protocol/packetDecode.go @@ -16,7 +16,8 @@ func DecodeFilePacket(filePacket *Packet) (*fsys.File, error) { if filePacket.Header != HeaderFile { return nil, ErrorWrongPacket } - // FILE~(idInBinary)(filenameLengthInBinary)(filename)(filesize)(checksumLengthInBinary)checksum + + //(id in binary)(filename length in binary)(filename)(filesize)(checksum length in binary)(checksum)(relative path to the upper directory size in binary if present)(relative path) // retrieve data from packet body @@ -64,12 +65,26 @@ func DecodeFilePacket(filePacket *Packet) (*fsys.File, error) { } checksum := string(checksumBytes) + // relative path + var relPathLength uint64 + err = binary.Read(packetReader, binary.BigEndian, &relPathLength) + if err != nil { + return nil, err + } + relPathBytes := make([]byte, relPathLength) + _, err = packetReader.Read(relPathBytes) + if err != nil { + return nil, err + } + relPath := string(relPathBytes) + return &fsys.File{ - ID: fileID, - Name: filename, - Size: filesize, - Checksum: checksum, - Handler: nil, + ID: fileID, + Name: filename, + Size: filesize, + Checksum: checksum, + RelativeParentPath: relPath, + Handler: nil, }, nil } diff --git a/src/protocol/protocol_test.go b/src/protocol/protocol_test.go index f7c0cdf..fb16ec8 100644 --- a/src/protocol/protocol_test.go +++ b/src/protocol/protocol_test.go @@ -6,8 +6,7 @@ import ( "testing" ) -// Practically tests the whole protocol -func TestTransfer(t *testing.T) { +func Test_WriteRead(t *testing.T) { packet := Packet{ Header: "randomheader", Body: []byte("fIlEnAmE.txt"), @@ -57,7 +56,7 @@ func TestTransfer(t *testing.T) { } } -func TestBytesToPacket(t *testing.T) { +func Test_BytesToPacket(t *testing.T) { packet := Packet{ Header: HeaderFileBytes, Body: []byte("fIlEnAmE.txt"),