]> git.r.bdr.sh - rbdr/mobius/blobdiff - hotline/transaction.go
Fix broken io.Reader implementations
[rbdr/mobius] / hotline / transaction.go
index 8d7d9ed43b60ac41a296bca25c9224ad1172c763..39dcd81f0c97dec02a8eb3425e491855706f19f3 100644 (file)
@@ -1,78 +1,78 @@
 package hotline
 
 import (
 package hotline
 
 import (
+       "bufio"
        "bytes"
        "encoding/binary"
        "errors"
        "fmt"
        "bytes"
        "encoding/binary"
        "errors"
        "fmt"
-       "github.com/jhalter/mobius/concat"
+       "io"
        "math/rand"
        "math/rand"
+       "slices"
 )
 
 const (
 )
 
 const (
-       tranError          = 0
-       tranGetMsgs        = 101
-       tranNewMsg         = 102
-       tranOldPostNews    = 103
-       tranServerMsg      = 104
-       tranChatSend       = 105
-       tranChatMsg        = 106
-       tranLogin          = 107
-       tranSendInstantMsg = 108
-       tranShowAgreement  = 109
-       tranDisconnectUser = 110
-       // tranDisconnectMsg        = 111 TODO: implement friendly disconnect
-       tranInviteNewChat        = 112
-       tranInviteToChat         = 113
-       tranRejectChatInvite     = 114
-       tranJoinChat             = 115
-       tranLeaveChat            = 116
-       tranNotifyChatChangeUser = 117
-       tranNotifyChatDeleteUser = 118
-       tranNotifyChatSubject    = 119
-       tranSetChatSubject       = 120
-       tranAgreed               = 121
-       tranServerBanner         = 122
-       tranGetFileNameList      = 200
-       tranDownloadFile         = 202
-       tranUploadFile           = 203
-       tranNewFolder            = 205
-       tranDeleteFile           = 204
-       tranGetFileInfo          = 206
-       tranSetFileInfo          = 207
-       tranMoveFile             = 208
-       tranMakeFileAlias        = 209
-       tranDownloadFldr         = 210
-       // tranDownloadInfo         = 211 TODO: implement file transfer queue
-       tranDownloadBanner     = 212
-       tranUploadFldr         = 213
-       tranGetUserNameList    = 300
-       tranNotifyChangeUser   = 301
-       tranNotifyDeleteUser   = 302
-       tranGetClientInfoText  = 303
-       tranSetClientUserInfo  = 304
-       tranListUsers          = 348
-       tranUpdateUser         = 349
-       tranNewUser            = 350
-       tranDeleteUser         = 351
-       tranGetUser            = 352
-       tranSetUser            = 353
-       tranUserAccess         = 354
-       tranUserBroadcast      = 355
-       tranGetNewsCatNameList = 370
-       tranGetNewsArtNameList = 371
-       tranDelNewsItem        = 380
-       tranNewNewsFldr        = 381
-       tranNewNewsCat         = 382
-       tranGetNewsArtData     = 400
-       tranPostNewsArt        = 410
-       tranDelNewsArt         = 411
-       tranKeepAlive          = 500
+       TranError                = 0
+       TranGetMsgs              = 101
+       TranNewMsg               = 102
+       TranOldPostNews          = 103
+       TranServerMsg            = 104
+       TranChatSend             = 105
+       TranChatMsg              = 106
+       TranLogin                = 107
+       TranSendInstantMsg       = 108
+       TranShowAgreement        = 109
+       TranDisconnectUser       = 110
+       TranDisconnectMsg        = 111 // TODO: implement server initiated friendly disconnect
+       TranInviteNewChat        = 112
+       TranInviteToChat         = 113
+       TranRejectChatInvite     = 114
+       TranJoinChat             = 115
+       TranLeaveChat            = 116
+       TranNotifyChatChangeUser = 117
+       TranNotifyChatDeleteUser = 118
+       TranNotifyChatSubject    = 119
+       TranSetChatSubject       = 120
+       TranAgreed               = 121
+       TranServerBanner         = 122
+       TranGetFileNameList      = 200
+       TranDownloadFile         = 202
+       TranUploadFile           = 203
+       TranNewFolder            = 205
+       TranDeleteFile           = 204
+       TranGetFileInfo          = 206
+       TranSetFileInfo          = 207
+       TranMoveFile             = 208
+       TranMakeFileAlias        = 209
+       TranDownloadFldr         = 210
+       TranDownloadInfo         = 211 // TODO: implement file transfer queue
+       TranDownloadBanner       = 212
+       TranUploadFldr           = 213
+       TranGetUserNameList      = 300
+       TranNotifyChangeUser     = 301
+       TranNotifyDeleteUser     = 302
+       TranGetClientInfoText    = 303
+       TranSetClientUserInfo    = 304
+       TranListUsers            = 348
+       TranUpdateUser           = 349
+       TranNewUser              = 350
+       TranDeleteUser           = 351
+       TranGetUser              = 352
+       TranSetUser              = 353
+       TranUserAccess           = 354
+       TranUserBroadcast        = 355
+       TranGetNewsCatNameList   = 370
+       TranGetNewsArtNameList   = 371
+       TranDelNewsItem          = 380
+       TranNewNewsFldr          = 381
+       TranNewNewsCat           = 382
+       TranGetNewsArtData       = 400
+       TranPostNewsArt          = 410
+       TranDelNewsArt           = 411
+       TranKeepAlive            = 500
 )
 
 type Transaction struct {
 )
 
 type Transaction struct {
-       clientID *[]byte
-
        Flags      byte   // Reserved (should be 0)
        IsReply    byte   // Request (0) or reply (1)
        Type       []byte // Requested operation (user defined)
        Flags      byte   // Reserved (should be 0)
        IsReply    byte   // Request (0) or reply (1)
        Type       []byte // Requested operation (user defined)
@@ -82,6 +82,9 @@ type Transaction struct {
        DataSize   []byte // Size of data in this transaction part. This allows splitting large transactions into smaller parts.
        ParamCount []byte // Number of the parameters for this transaction
        Fields     []Field
        DataSize   []byte // Size of data in this transaction part. This allows splitting large transactions into smaller parts.
        ParamCount []byte // Number of the parameters for this transaction
        Fields     []Field
+
+       clientID   *[]byte // Internal identifier for target client
+       readOffset int     // Internal offset to track read progress
 }
 
 func NewTransaction(t int, clientID *[]byte, fields ...Field) *Transaction {
 }
 
 func NewTransaction(t int, clientID *[]byte, fields ...Field) *Transaction {
@@ -113,9 +116,19 @@ func (t *Transaction) Write(p []byte) (n int, err error) {
        if tranLen > len(p) {
                return n, errors.New("buflen too small for tranLen")
        }
        if tranLen > len(p) {
                return n, errors.New("buflen too small for tranLen")
        }
-       fields, err := ReadFields(p[20:22], p[22:tranLen])
-       if err != nil {
-               return n, err
+
+       // Create a new scanner for parsing incoming bytes into transaction tokens
+       scanner := bufio.NewScanner(bytes.NewReader(p[22:tranLen]))
+       scanner.Split(fieldScanner)
+
+       for i := 0; i < int(binary.BigEndian.Uint16(p[20:22])); i++ {
+               scanner.Scan()
+
+               var field Field
+               if _, err := field.Write(scanner.Bytes()); err != nil {
+                       return 0, fmt.Errorf("error reading field: %w", err)
+               }
+               t.Fields = append(t.Fields, field)
        }
 
        t.Flags = p[0]
        }
 
        t.Flags = p[0]
@@ -126,7 +139,6 @@ func (t *Transaction) Write(p []byte) (n int, err error) {
        t.TotalSize = p[12:16]
        t.DataSize = p[16:20]
        t.ParamCount = p[20:22]
        t.TotalSize = p[12:16]
        t.DataSize = p[16:20]
        t.ParamCount = p[20:22]
-       t.Fields = fields
 
        return len(p), err
 }
 
        return len(p), err
 }
@@ -177,8 +189,8 @@ func ReadFields(paramCount []byte, buf []byte) ([]Field, error) {
                }
 
                fields = append(fields, Field{
                }
 
                fields = append(fields, Field{
-                       ID:        fieldID,
-                       FieldSize: fieldSize,
+                       ID:        [2]byte(fieldID),
+                       FieldSize: [2]byte(fieldSize),
                        Data:      buf[4 : 4+fieldSizeInt],
                })
 
                        Data:      buf[4 : 4+fieldSizeInt],
                })
 
@@ -192,18 +204,24 @@ func ReadFields(paramCount []byte, buf []byte) ([]Field, error) {
        return fields, nil
 }
 
        return fields, nil
 }
 
-func (t *Transaction) MarshalBinary() (data []byte, err error) {
+// Read implements the io.Reader interface for Transaction
+func (t *Transaction) Read(p []byte) (int, error) {
        payloadSize := t.Size()
 
        fieldCount := make([]byte, 2)
        binary.BigEndian.PutUint16(fieldCount, uint16(len(t.Fields)))
 
        payloadSize := t.Size()
 
        fieldCount := make([]byte, 2)
        binary.BigEndian.PutUint16(fieldCount, uint16(len(t.Fields)))
 
-       var fieldPayload []byte
+       bbuf := new(bytes.Buffer)
+
        for _, field := range t.Fields {
        for _, field := range t.Fields {
-               fieldPayload = append(fieldPayload, field.Payload()...)
+               f := field
+               _, err := bbuf.ReadFrom(&f)
+               if err != nil {
+                       return 0, fmt.Errorf("error reading field: %w", err)
+               }
        }
 
        }
 
-       return concat.Slices(
+       buf := slices.Concat(
                []byte{t.Flags, t.IsReply},
                t.Type,
                t.ID,
                []byte{t.Flags, t.IsReply},
                t.Type,
                t.ID,
@@ -211,8 +229,17 @@ func (t *Transaction) MarshalBinary() (data []byte, err error) {
                payloadSize,
                payloadSize, // this is the dataSize field, but seeming the same as totalSize
                fieldCount,
                payloadSize,
                payloadSize, // this is the dataSize field, but seeming the same as totalSize
                fieldCount,
-               fieldPayload,
-       ), err
+               bbuf.Bytes(),
+       )
+
+       if t.readOffset >= len(buf) {
+               return 0, io.EOF // All bytes have been read
+       }
+
+       n := copy(p, buf[t.readOffset:])
+       t.readOffset += n
+
+       return n, nil
 }
 
 // Size returns the total size of the transaction payload
 }
 
 // Size returns the total size of the transaction payload
@@ -231,7 +258,7 @@ func (t *Transaction) Size() []byte {
 
 func (t *Transaction) GetField(id int) Field {
        for _, field := range t.Fields {
 
 func (t *Transaction) GetField(id int) Field {
        for _, field := range t.Fields {
-               if id == int(binary.BigEndian.Uint16(field.ID)) {
+               if id == int(binary.BigEndian.Uint16(field.ID[:])) {
                        return field
                }
        }
                        return field
                }
        }