- s.Stats.DownloadCounter += 1
- s.Stats.DownloadsInProgress += 1
- defer func() { s.Stats.DownloadsInProgress -= 1 }()
-
- // Folder Download flow:
- // 1. Get filePath from the transfer
- // 2. Iterate over files
- // 3. For each fileWrapper:
- // Send fileWrapper header to client
- // The client can reply in 3 ways:
- //
- // 1. If type is an odd number (unknown type?), or fileWrapper download for the current fileWrapper is completed:
- // client sends []byte{0x00, 0x03} to tell the server to continue to the next fileWrapper
- //
- // 2. If download of a fileWrapper is to be resumed:
- // client sends:
- // []byte{0x00, 0x02} // download folder action
- // [2]byte // Resume data size
- // []byte fileWrapper resume data (see myField_FileResumeData)
- //
- // 3. Otherwise, download of the fileWrapper is requested and client sends []byte{0x00, 0x01}
- //
- // When download is requested (case 2 or 3), server replies with:
- // [4]byte - fileWrapper size
- // []byte - Flattened File Object
- //
- // After every fileWrapper download, client could request next fileWrapper with:
- // []byte{0x00, 0x03}
- //
- // This notifies the server to send the next item header
-
- basePathLen := len(fullPath)
-
- rLogger.Info("Start folder download", "path", fullPath)
-
- nextAction := make([]byte, 2)
- if _, err := io.ReadFull(rwc, nextAction); err != nil {
- return err
- }
-
- i := 0
- err = filepath.Walk(fullPath+"/", func(path string, info os.FileInfo, err error) error {
- s.Stats.DownloadCounter += 1
- i += 1
-
- if err != nil {
- return err
- }
-
- // skip dot files
- if strings.HasPrefix(info.Name(), ".") {
- return nil
- }
-
- hlFile, err := newFileWrapper(s.FS, path, 0)
- if err != nil {
- return err
- }
-
- subPath := path[basePathLen+1:]
- rLogger.Debug("Sending fileheader", "i", i, "path", path, "fullFilePath", fullPath, "subPath", subPath, "IsDir", info.IsDir())
-
- if i == 1 {
- return nil
- }
-
- fileHeader := NewFileHeader(subPath, info.IsDir())
- if _, err := io.Copy(rwc, &fileHeader); err != nil {
- return fmt.Errorf("error sending file header: %w", err)
- }
-
- // Read the client's Next Action request
- if _, err := io.ReadFull(rwc, nextAction); err != nil {
- return err
- }
-
- rLogger.Debug("Client folder download action", "action", fmt.Sprintf("%X", nextAction[0:2]))
-
- var dataOffset int64
-
- switch nextAction[1] {
- case dlFldrActionResumeFile:
- // get size of resumeData
- resumeDataByteLen := make([]byte, 2)
- if _, err := io.ReadFull(rwc, resumeDataByteLen); err != nil {
- return err
- }
-
- resumeDataLen := binary.BigEndian.Uint16(resumeDataByteLen)
- resumeDataBytes := make([]byte, resumeDataLen)
- if _, err := io.ReadFull(rwc, resumeDataBytes); err != nil {
- return err
- }
-
- var frd FileResumeData
- if err := frd.UnmarshalBinary(resumeDataBytes); err != nil {
- return err
- }
- dataOffset = int64(binary.BigEndian.Uint32(frd.ForkInfoList[0].DataSize[:]))
- case dlFldrActionNextFile:
- // client asked to skip this file
- return nil
- }
-
- if info.IsDir() {
- return nil
- }
-
- rLogger.Info("File download started",
- "fileName", info.Name(),
- "TransferSize", fmt.Sprintf("%x", hlFile.ffo.TransferSize(dataOffset)),
- )
-
- // Send file size to client
- if _, err := rwc.Write(hlFile.ffo.TransferSize(dataOffset)); err != nil {
- s.Logger.Error(err.Error())
- return err
- }
-
- // Send ffo bytes to client
- _, err = io.Copy(rwc, hlFile.ffo)
- if err != nil {
- return err
- }
-
- file, err := s.FS.Open(path)
- if err != nil {
- return err
- }
-
- // wr := bufio.NewWriterSize(rwc, 1460)
- if _, err = io.Copy(rwc, io.TeeReader(file, fileTransfer.bytesSentCounter)); err != nil {
- return err
- }
-
- if nextAction[1] != 2 && hlFile.ffo.FlatFileHeader.ForkCount[1] == 3 {
- err = binary.Write(rwc, binary.BigEndian, hlFile.rsrcForkHeader())
- if err != nil {
- return err
- }
-
- rFile, err := hlFile.rsrcForkFile()
- if err != nil {
- return err
- }
-
- if _, err = io.Copy(rwc, io.TeeReader(rFile, fileTransfer.bytesSentCounter)); err != nil {
- return err
- }
- }
-
- // Read the client's Next Action request. This is always 3, I think?
- if _, err := io.ReadFull(rwc, nextAction); err != nil {
- return err
- }
-
- return nil
- })