package hotline
import (
- "encoding/binary"
"github.com/jhalter/mobius/concat"
)
Access *[]byte `yaml:"Access"` // 8 byte bitmap
}
-// Payload marshals an account to byte slice
-// Example:
-// 00 04 // fieldCount?
-// 00 66 // 102 - fieldUserName
-// 00 0d // 13
-// 61 64 6d 69 6e 69 73 74 72 61 74 6f 72 // administrator
-// 00 69 // 105 fieldUserLogin (encoded)
-// 00 05 // len
-// 9e 9b 92 96 91 // encoded login name
-// 00 6a // 106 fieldUserPassword
-// 00 01 // len
-// 78
-// 00 6e // fieldUserAccess
-// 00 08
-// ff d3 cf ef ff 80 00 00
-func (a *Account) Payload() (out []byte) {
- nameLen := make([]byte, 2)
- binary.BigEndian.PutUint16(nameLen, uint16(len(a.Name)))
-
- loginLen := make([]byte, 2)
- binary.BigEndian.PutUint16(loginLen, uint16(len(a.Login)))
-
+// MarshalBinary marshals an Account to byte slice
+func (a *Account) MarshalBinary() (out []byte) {
return concat.Slices(
[]byte{0x00, 0x3}, // param count -- always 3
-
- []byte{0x00, 0x66}, // fieldUserName
- nameLen,
- []byte(a.Name),
-
- []byte{0x00, 0x69}, // fieldUserLogin
- loginLen,
- negateString([]byte(a.Login)),
-
- []byte{0x00, 0x6e}, // fieldUserAccess
- []byte{0x00, 0x08},
- *a.Access,
+ NewField(fieldUserName, []byte(a.Name)).Payload(),
+ NewField(fieldUserLogin, negateString([]byte(a.Login))).Payload(),
+ NewField(fieldUserAccess, *a.Access).Payload(),
)
}
return nil
}
-func (c *Client) Connected() bool {
- // c.Agreed == true &&
- if c.UserAccess != nil {
- return true
- }
- return false
-}
-
func (c *Client) Disconnect() error {
err := c.Connection.Close()
if err != nil {
return fh
}
-func (fh *FileHeader) Read(p []byte) (n int, err error) {
- p = concat.Slices(
- fh.Size,
- fh.Type,
- fh.FilePath,
- )
- return len(p), nil
-}
-
func (fh *FileHeader) Payload() []byte {
return concat.Slices(
fh.Size,
}
return strings.Join(out, pathSeparator)
}
+
+func ReadFilePath(filePathFieldData []byte) string {
+ var fp FilePath
+ err := fp.UnmarshalBinary(filePathFieldData)
+ if err != nil {
+ // TODO
+ }
+ return fp.String()
+}
package hotline
-import "fmt"
+import (
+ "encoding/binary"
+ "fmt"
+ "strings"
+)
// File transfer types
const (
return out
}
+
+// 00 28 // DataSize
+// 00 00 // IsFolder
+// 00 02 // PathItemCount
+//
+// 00 00
+// 09
+// 73 75 62 66 6f 6c 64 65 72 // "subfolder"
+//
+// 00 00
+// 15
+// 73 75 62 66 6f 6c 64 65 72 2d 74 65 73 74 66 69 6c 65 2d 35 6b // "subfolder-testfile-5k"
+func readFolderUpload(buf []byte) folderUpload {
+ dataLen := binary.BigEndian.Uint16(buf[0:2])
+
+ fu := folderUpload{
+ DataSize: [2]byte{buf[0], buf[1]}, // Size of this structure (not including data size element itself)
+ IsFolder: [2]byte{buf[2], buf[3]},
+ PathItemCount: [2]byte{buf[4], buf[5]},
+ FileNamePath: buf[6 : dataLen+2],
+ }
+
+ return fu
+}
+
+
+func (fu *folderUpload) UnmarshalBinary(b []byte) error {
+ fu.DataSize = [2]byte{b[0], b[1]}
+ fu.IsFolder = [2]byte{b[2], b[3]}
+ fu.PathItemCount = [2]byte{b[4], b[5]}
+
+ return nil
+}
+
+type folderUpload struct {
+ DataSize [2]byte
+ IsFolder [2]byte
+ PathItemCount [2]byte
+ FileNamePath []byte
+}
+
+func (fu *folderUpload) FormattedPath() string {
+ pathItemLen := binary.BigEndian.Uint16(fu.PathItemCount[:])
+
+ var pathSegments []string
+ pathData := fu.FileNamePath
+
+ for i := uint16(0); i < pathItemLen; i++ {
+ segLen := pathData[2]
+ pathSegments = append(pathSegments, string(pathData[3:3+segLen]))
+ pathData = pathData[3+segLen:]
+ }
+
+ return strings.Join(pathSegments, pathSeparator)
+}
return code
}
-func getFileNameList(filePath string) ([]Field, error) {
- var fields []Field
-
+func getFileNameList(filePath string) (fields []Field, err error) {
files, err := ioutil.ReadDir(filePath)
if err != nil {
return fields, nil
return bytes
}
-
-func ReadFilePath(filePathFieldData []byte) string {
- var fp FilePath
- err := fp.UnmarshalBinary(filePathFieldData)
- if err != nil {
- // TODO
- }
- return fp.String()
-}
}
func (ffo flattenedFileObject) TransferSize() []byte {
- payloadSize := len(ffo.Payload())
+ payloadSize := len(ffo.BinaryMarshal())
dataSize := binary.BigEndian.Uint32(ffo.FlatFileDataForkHeader.DataSize)
transferSize := make([]byte, 4)
DataSize []byte
}
-func NewFlatFileDataForkHeader() FlatFileDataForkHeader {
- return FlatFileDataForkHeader{
- ForkType: []byte("DATA"),
- CompressionType: []byte{0, 0, 0, 0},
- RSVD: []byte{0, 0, 0, 0},
- // DataSize: []byte{0, 0, 0x03, 0xc3},
- }
-}
-
// ReadFlattenedFileObject parses a byte slice into a flattenedFileObject
func ReadFlattenedFileObject(bytes []byte) flattenedFileObject {
nameSize := bytes[110:112]
return ffo
}
-func (f flattenedFileObject) Payload() []byte {
+func (f flattenedFileObject) BinaryMarshal() []byte {
var out []byte
out = append(out, f.FlatFileHeader.Format[:]...)
out = append(out, f.FlatFileHeader.Version[:]...)
package hotline
import (
- "bytes"
"context"
"encoding/binary"
"errors"
c.Server.Stats.LoginCount += 1
const readBuffSize = 1024000 // 1KB - TODO: what should this be?
- const maxTranSize = 1024000
tranBuff := make([]byte, 0)
tReadlen := 0
// Infinite loop where take action on incoming client requests until the connection is closed
s.Logger.Infow("File download started", "filePath", fullFilePath, "transactionRef", fileTransfer.ReferenceNumber, "RemoteAddr", conn.RemoteAddr().String())
// Start by sending flat file object to client
- if _, err := conn.Write(ffo.Payload()); err != nil {
+ if _, err := conn.Write(ffo.BinaryMarshal()); err != nil {
return err
}
}
ffo := ReadFlattenedFileObject(buf)
- payloadLen := len(ffo.Payload())
+ payloadLen := len(ffo.BinaryMarshal())
fileSize := int(binary.BigEndian.Uint32(ffo.FlatFileDataForkHeader.DataSize))
destinationFile := s.Config.FileRoot + ReadFilePath(fileTransfer.FilePath) + "/" + string(fileTransfer.FileName)
// Read the client's Next Action request
//TODO: Remove hardcoded behavior and switch behaviors based on the next action send
if _, err := conn.Read(readBuffer); err != nil {
- s.Logger.Errorf("error reading next action: %v", err)
return err
}
}
splitPath := strings.Split(path, "/")
- //strings.Join(splitPath[:len(splitPath)-1], "/")
ffo, err := NewFlattenedFileObject(strings.Join(splitPath[:len(splitPath)-1], "/"), info.Name())
if err != nil {
}
// Send file bytes to client
- if _, err := conn.Write(ffo.Payload()); err != nil {
+ if _, err := conn.Write(ffo.BinaryMarshal()); err != nil {
s.Logger.Error(err)
return err
}
}
sendBuffer := make([]byte, 1048576)
- totalBytesSent := len(ffo.Payload())
+ totalBytesSent := len(ffo.BinaryMarshal())
for {
bytesRead, err := file.Read(sendBuffer)
"RemoteAddr", conn.RemoteAddr().String(),
"FormattedPath", fu.FormattedPath(),
"IsFolder", fmt.Sprintf("%x", fu.IsFolder),
- "PathItemCount", binary.BigEndian.Uint16(fu.PathItemCount),
+ "PathItemCount", binary.BigEndian.Uint16(fu.PathItemCount[:]),
)
- if bytes.Equal(fu.IsFolder, []byte{0, 1}) {
+ if fu.IsFolder == [2]byte{0, 1} {
if _, err := os.Stat(dstPath + "/" + fu.FormattedPath()); os.IsNotExist(err) {
s.Logger.Infow("Target path does not exist; Creating...", "dstPath", dstPath)
if err := os.Mkdir(dstPath+"/"+fu.FormattedPath(), 0777); err != nil {
return err
}
ffo := ReadFlattenedFileObject(buf)
- payloadLen := len(ffo.Payload())
+ payloadLen := len(ffo.BinaryMarshal())
fileSize := int(binary.BigEndian.Uint32(ffo.FlatFileDataForkHeader.DataSize))
newFile, err := os.Create(dst)
}
}
-// 00 28 // DataSize
-// 00 00 // IsFolder
-// 00 02 // PathItemCount
-//
-// 00 00
-// 09
-// 73 75 62 66 6f 6c 64 65 72 // "subfolder"
-//
-// 00 00
-// 15
-// 73 75 62 66 6f 6c 64 65 72 2d 74 65 73 74 66 69 6c 65 2d 35 6b // "subfolder-testfile-5k"
-func readFolderUpload(buf []byte) folderUpload {
- dataLen := binary.BigEndian.Uint16(buf[0:2])
-
- fu := folderUpload{
- DataSize: buf[0:2], // Size of this structure (not including data size element itself)
- IsFolder: buf[2:4],
- PathItemCount: buf[4:6],
- FileNamePath: buf[6 : dataLen+2],
- }
-
- return fu
-}
-
-type folderUpload struct {
- DataSize []byte
- IsFolder []byte
- PathItemCount []byte
- FileNamePath []byte
-}
-
-func (fu *folderUpload) FormattedPath() string {
- pathItemLen := binary.BigEndian.Uint16(fu.PathItemCount)
-
- var pathSegments []string
- pathData := fu.FileNamePath
-
- for i := uint16(0); i < pathItemLen; i++ {
- segLen := pathData[2]
- pathSegments = append(pathSegments, string(pathData[3:3+segLen]))
- pathData = pathData[3+segLen:]
- }
-
- return strings.Join(pathSegments, pathSeparator)
-}
// sortedClients is a utility function that takes a map of *ClientConn and returns a sorted slice of the values.
// The purpose of this is to ensure that the ordering of client connections is deterministic so that test assertions work.
}
type ServerRecord struct {
- IPAddr []byte
- Port []byte
- NumUsers []byte // Number of users connected to this particular server
- Unused []byte
+ IPAddr [4]byte
+ Port [2]byte
+ NumUsers [2]byte // Number of users connected to this particular server
+ Unused [2]byte
NameSize byte // Length of name string
- Name []byte // Server’s name
+ Name []byte // Server name
DescriptionSize byte
Description []byte
}
}
func (s *ServerRecord) Read(b []byte) (n int, err error) {
- s.IPAddr = b[0:4]
- s.Port = b[4:6]
- s.NumUsers = b[6:8]
- s.NameSize = b[10]
+ copy(s.IPAddr[:], b[0:4])
+ copy(s.Port[:], b[4:6])
+ copy(s.NumUsers[:], b[6:8])
nameLen := int(b[10])
+
s.Name = b[11 : 11+nameLen]
s.DescriptionSize = b[11+nameLen]
s.Description = b[12+nameLen : 12+nameLen+int(s.DescriptionSize)]
return 12 + nameLen + int(s.DescriptionSize), nil
}
+//
+//func (s *ServerRecord) UnmarshalBinary(b []byte) (err error) {
+// r := bytes.NewReader(b[:10])
+// if err := binary.Read(r, binary.BigEndian, s); err != nil {
+// return err
+// }
+//
+// copy(s.IPAddr[:], b[0:4])
+// s.Port = b[4:6]
+// s.NumUsers = b[6:8]
+// s.NameSize = b[10]
+// nameLen := int(b[10])
+// s.Name = b[11 : 11+nameLen]
+// s.DescriptionSize = b[11+nameLen]
+// s.Description = b[12+nameLen : 12+nameLen+int(s.DescriptionSize)]
+//
+// return nil
+//}
+
func (s *ServerRecord) PortInt() int {
- data := binary.BigEndian.Uint16(s.Port)
+ data := binary.BigEndian.Uint16(s.Port[:])
return int(data)
}
func (s *ServerRecord) Addr() string {
return fmt.Sprintf("%s:%s",
- net.IP(s.IPAddr),
- strconv.Itoa(int(binary.BigEndian.Uint16(s.Port))),
+ net.IP(s.IPAddr[:]),
+ strconv.Itoa(int(binary.BigEndian.Uint16(s.Port[:]))),
)
}
var userFields []Field
// TODO: make order deterministic
for _, acc := range cc.Server.Accounts {
- userField := acc.Payload()
+ userField := acc.MarshalBinary()
userFields = append(userFields, NewField(fieldData, userField))
}
cc.Transfers[FolderDownload] = append(cc.Transfers[FolderDownload], fileTransfer)
var fp FilePath
- fp.UnmarshalBinary(t.GetField(fieldFilePath).Data)
+ err = fp.UnmarshalBinary(t.GetField(fieldFilePath).Data)
+ if err != nil {
+ return res, err
+ }
fullFilePath := fmt.Sprintf("%v%v", cc.Server.Config.FileRoot+fp.String(), string(fileTransfer.FileName))
transferSize, err := CalcTotalSize(fullFilePath)