X-Git-Url: https://git.r.bdr.sh/rbdr/mobius/blobdiff_plain/decc2fbf5db4a05aec93462ad35d890930bddd04..af0e8409bd0eb3bbd97ce8d2a249344ac4d2894d:/hotline/transaction.go diff --git a/hotline/transaction.go b/hotline/transaction.go index 2994785..8d7d9ed 100644 --- a/hotline/transaction.go +++ b/hotline/transaction.go @@ -1,12 +1,12 @@ package hotline import ( + "bytes" "encoding/binary" "errors" "fmt" "github.com/jhalter/mobius/concat" "math/rand" - "net" ) const ( @@ -32,6 +32,7 @@ const ( tranNotifyChatSubject = 119 tranSetChatSubject = 120 tranAgreed = 121 + tranServerBanner = 122 tranGetFileNameList = 200 tranDownloadFile = 202 tranUploadFile = 203 @@ -40,18 +41,18 @@ const ( tranGetFileInfo = 206 tranSetFileInfo = 207 tranMoveFile = 208 - tranMakeFileAlias = 209 // TODO: implement file alias command + tranMakeFileAlias = 209 tranDownloadFldr = 210 // tranDownloadInfo = 211 TODO: implement file transfer queue - // tranDownloadBanner = 212 TODO: figure out what this is used for - tranUploadFldr = 213 - tranGetUserNameList = 300 - tranNotifyChangeUser = 301 - tranNotifyDeleteUser = 302 - tranGetClientInfoText = 303 - tranSetClientUserInfo = 304 - tranListUsers = 348 - // tranUpdateUser = 349 TODO: implement user updates from the > 1.5 account editor + tranDownloadBanner = 212 + tranUploadFldr = 213 + tranGetUserNameList = 300 + tranNotifyChangeUser = 301 + tranNotifyDeleteUser = 302 + tranGetClientInfoText = 303 + tranSetClientUserInfo = 304 + tranListUsers = 348 + tranUpdateUser = 349 tranNewUser = 350 tranDeleteUser = 351 tranGetUser = 352 @@ -101,76 +102,53 @@ func NewTransaction(t int, clientID *[]byte, fields ...Field) *Transaction { } } -// ReadTransaction parses a byte slice into a struct. The input slice may be shorter or longer -// that the transaction size depending on what was read from the network connection. -func ReadTransaction(buf []byte) (*Transaction, int, error) { - totalSize := binary.BigEndian.Uint32(buf[12:16]) +// Write implements io.Writer interface for Transaction +func (t *Transaction) Write(p []byte) (n int, err error) { + totalSize := binary.BigEndian.Uint32(p[12:16]) // the buf may include extra bytes that are not part of the transaction // tranLen represents the length of bytes that are part of the transaction tranLen := int(20 + totalSize) - if tranLen > len(buf) { - return nil, 0, errors.New("buflen too small for tranLen") + if tranLen > len(p) { + return n, errors.New("buflen too small for tranLen") } - fields, err := ReadFields(buf[20:22], buf[22:tranLen]) + fields, err := ReadFields(p[20:22], p[22:tranLen]) if err != nil { - return nil, 0, err + return n, err } - return &Transaction{ - Flags: buf[0], - IsReply: buf[1], - Type: buf[2:4], - ID: buf[4:8], - ErrorCode: buf[8:12], - TotalSize: buf[12:16], - DataSize: buf[16:20], - ParamCount: buf[20:22], - Fields: fields, - }, tranLen, nil + t.Flags = p[0] + t.IsReply = p[1] + t.Type = p[2:4] + t.ID = p[4:8] + t.ErrorCode = p[8:12] + t.TotalSize = p[12:16] + t.DataSize = p[16:20] + t.ParamCount = p[20:22] + t.Fields = fields + + return len(p), err } -func readN(conn net.Conn, n int) ([]Transaction, error) { - buf := make([]byte, 1400) - i := 0 - for { - readLen, err := conn.Read(buf) - if err != nil { - return nil, err - } - - transactions, _, err := readTransactions(buf[:readLen]) - // spew.Fdump(os.Stderr, transactions) - if err != nil { - return nil, err - } +const tranHeaderLen = 20 // fixed length of transaction fields before the variable length fields - i += len(transactions) - - if n == i { - return transactions, nil - } +// transactionScanner implements bufio.SplitFunc for parsing incoming byte slices into complete tokens +func transactionScanner(data []byte, _ bool) (advance int, token []byte, err error) { + // The bytes that contain the size of a transaction are from 12:16, so we need at least 16 bytes + if len(data) < 16 { + return 0, nil, nil } -} -func readTransactions(buf []byte) ([]Transaction, int, error) { - var transactions []Transaction + totalSize := binary.BigEndian.Uint32(data[12:16]) - bufLen := len(buf) - - var bytesRead = 0 - for bytesRead < bufLen { - t, tReadLen, err := ReadTransaction(buf[bytesRead:]) - if err != nil { - return transactions, bytesRead, err - } - bytesRead += tReadLen - - transactions = append(transactions, *t) + // tranLen represents the length of bytes that are part of the transaction + tranLen := int(tranHeaderLen + totalSize) + if tranLen > len(data) { + return 0, nil, nil } - return transactions, bytesRead, nil + return tranLen, data[0:tranLen], nil } const minFieldLen = 4 @@ -214,7 +192,7 @@ func ReadFields(paramCount []byte, buf []byte) ([]Field, error) { return fields, nil } -func (t Transaction) MarshalBinary() (data []byte, err error) { +func (t *Transaction) MarshalBinary() (data []byte, err error) { payloadSize := t.Size() fieldCount := make([]byte, 2) @@ -238,7 +216,7 @@ func (t Transaction) MarshalBinary() (data []byte, err error) { } // Size returns the total size of the transaction payload -func (t Transaction) Size() []byte { +func (t *Transaction) Size() []byte { bs := make([]byte, 4) fieldSize := 0 @@ -251,7 +229,7 @@ func (t Transaction) Size() []byte { return bs } -func (t Transaction) GetField(id int) Field { +func (t *Transaction) GetField(id int) Field { for _, field := range t.Fields { if id == int(binary.BigEndian.Uint16(field.ID)) { return field @@ -260,3 +238,7 @@ func (t Transaction) GetField(id int) Field { return Field{} } + +func (t *Transaction) IsError() bool { + return bytes.Equal(t.ErrorCode, []byte{0, 0, 0, 1}) +}