]> git.r.bdr.sh - rbdr/mobius/commitdiff
Fix broken io.Reader implementations
authorJeff Halter <redacted>
Sun, 16 Jun 2024 23:03:54 +0000 (16:03 -0700)
committerJeff Halter <redacted>
Sun, 16 Jun 2024 23:05:25 +0000 (16:05 -0700)
hotline/account.go
hotline/client_conn.go
hotline/file_transfer.go
hotline/news.go
hotline/news_test.go
hotline/server.go
hotline/transaction.go
hotline/transaction_handlers.go
hotline/transaction_handlers_test.go

index 3ad068797294c91e5b8e159a64364d0cdf77ac48..18965ed5ea1b4a9ae5cb711eff4c0d181a11c255 100644 (file)
@@ -15,7 +15,7 @@ type Account struct {
        Login    string       `yaml:"Login"`
        Name     string       `yaml:"Name"`
        Password string       `yaml:"Password"`
-       Access   accessBitmap `yaml:"Access"`
+       Access   accessBitmap `yaml:"Access,flow"`
 
        readOffset int // Internal offset to track read progress
 }
index 8e6621864edc07a13fd2091c988f0a791c30c2ea..e21d8b180e7e004537afca659284f7c2ea76b06f 100644 (file)
@@ -167,7 +167,6 @@ func (cc *ClientConn) notifyOthers(t Transaction) (trans []Transaction) {
 // NewReply returns a reply Transaction with fields for the ClientConn
 func (cc *ClientConn) NewReply(t *Transaction, fields ...Field) Transaction {
        return Transaction{
-               Flags:     0x00,
                IsReply:   0x01,
                Type:      []byte{0x00, 0x00},
                ID:        t.ID,
@@ -181,7 +180,6 @@ func (cc *ClientConn) NewReply(t *Transaction, fields ...Field) Transaction {
 func (cc *ClientConn) NewErrReply(t *Transaction, errMsg string) Transaction {
        return Transaction{
                clientID:  cc.ID,
-               Flags:     0x00,
                IsReply:   0x01,
                Type:      []byte{0, 0},
                ID:        t.ID,
index ba4a1e1b3f12a621a6c8067bee4d61ca4e56e52c..7c24109e1dfb9e1f70f0938764d2614464ffb634 100644 (file)
@@ -11,11 +11,11 @@ import (
 
 // File transfer types
 const (
-       FileDownload   = 0
-       FileUpload     = 1
-       FolderDownload = 2
-       FolderUpload   = 3
-       bannerDownload = 4
+       FileDownload = iota
+       FileUpload
+       FolderDownload
+       FolderUpload
+       bannerDownload
 )
 
 type FileTransfer struct {
index e6b756779886bf8e6c2fabe4bc4cba31b1d03e45..cd3b6af5d6a5b46d6c5bcd33323f461c19dcbe43 100644 (file)
@@ -2,7 +2,6 @@ package hotline
 
 import (
        "bytes"
-       "crypto/rand"
        "encoding/binary"
        "io"
        "slices"
@@ -17,20 +16,19 @@ const defaultNewsTemplate = `From %s (%s):
 
 __________________________________________________________`
 
+// ThreadedNews is the top level struct containing all threaded news categories, bundles, and articles
 type ThreadedNews struct {
        Categories map[string]NewsCategoryListData15 `yaml:"Categories"`
 }
 
 type NewsCategoryListData15 struct {
-       Type     [2]byte `yaml:"Type"` // Size 2 ; Bundle (2) or category (3)
-       Count    []byte  // Article or SubCategory count Size 2
-       NameSize byte
-       Name     string                            `yaml:"Name"`     //
+       Type     [2]byte                           `yaml:"Type,flow"` // Bundle (2) or category (3)
+       Name     string                            `yaml:"Name"`
        Articles map[uint32]*NewsArtData           `yaml:"Articles"` // Optional, if Type is Category
        SubCats  map[string]NewsCategoryListData15 `yaml:"SubCats"`
-       GUID     []byte                            // Size 16
-       AddSN    []byte                            // Size 4
-       DeleteSN []byte                            // Size 4
+       GUID     [16]byte                          `yaml:"-"` // What does this do?  Undocumented and seeming unused.
+       AddSN    [4]byte                           `yaml:"-"` // What does this do?  Undocumented and seeming unused.
+       DeleteSN [4]byte                           `yaml:"-"` // What does this do?  Undocumented and seeming unused.
 }
 
 func (newscat *NewsCategoryListData15) GetNewsArtListData() NewsArtListData {
@@ -42,15 +40,14 @@ func (newscat *NewsCategoryListData15) GetNewsArtListData() NewsArtListData {
                binary.BigEndian.PutUint32(id, i)
 
                newArt := NewsArtList{
-                       ID:          id,
-                       TimeStamp:   art.Date[:],
-                       ParentID:    art.ParentArt[:],
-                       Flags:       []byte{0, 0, 0, 0},
-                       FlavorCount: []byte{0, 0},
+                       ID:          [4]byte(id),
+                       TimeStamp:   art.Date,
+                       ParentID:    art.ParentArt,
                        Title:       []byte(art.Title),
                        Poster:      []byte(art.Poster),
                        ArticleSize: art.DataSize(),
                }
+
                newsArts = append(newsArts, newArt)
        }
 
@@ -60,31 +57,29 @@ func (newscat *NewsCategoryListData15) GetNewsArtListData() NewsArtListData {
                b, err := io.ReadAll(&v)
                if err != nil {
                        // TODO
+                       panic(err)
                }
                newsArtsPayload = append(newsArtsPayload, b...)
        }
 
-       nald := NewsArtListData{
-               ID:          [4]byte{0, 0, 0, 0},
+       return NewsArtListData{
                Count:       len(newsArts),
                Name:        []byte{},
                Description: []byte{},
                NewsArtList: newsArtsPayload,
        }
-
-       return nald
 }
 
 // NewsArtData represents single news article
 type NewsArtData struct {
        Title         string  `yaml:"Title"`
        Poster        string  `yaml:"Poster"`
-       Date          [8]byte `yaml:"Date"`             // size 8
-       PrevArt       [4]byte `yaml:"PrevArt"`          // size 4
-       NextArt       [4]byte `yaml:"NextArt"`          // size 4
-       ParentArt     [4]byte `yaml:"ParentArt"`        // size 4
-       FirstChildArt [4]byte `yaml:"FirstChildArtArt"` // size 4
-       DataFlav      []byte  `yaml:"DataFlav"`         // "text/plain"
+       Date          [8]byte `yaml:"Date,flow"`
+       PrevArt       [4]byte `yaml:"PrevArt,flow"`
+       NextArt       [4]byte `yaml:"NextArt,flow"`
+       ParentArt     [4]byte `yaml:"ParentArt,flow"`
+       FirstChildArt [4]byte `yaml:"FirstChildArtArt,flow"`
+       DataFlav      []byte  `yaml:"-"` // "text/plain"
        Data          string  `yaml:"Data"`
 }
 
@@ -96,39 +91,45 @@ func (art *NewsArtData) DataSize() []byte {
 }
 
 type NewsArtListData struct {
-       ID          [4]byte `yaml:"ID"` // Size 4
+       ID          [4]byte `yaml:"ID"`
        Name        []byte  `yaml:"Name"`
        Description []byte  `yaml:"Description"` // not used?
        NewsArtList []byte  // List of articles                 Optional (if article count > 0)
        Count       int
+
+       readOffset int // Internal offset to track read progress
 }
 
 func (nald *NewsArtListData) Read(p []byte) (int, error) {
        count := make([]byte, 4)
        binary.BigEndian.PutUint32(count, uint32(nald.Count))
 
-       return copy(
-                       p,
-                       slices.Concat(
-                               nald.ID[:],
-                               count,
-                               []byte{uint8(len(nald.Name))},
-                               nald.Name,
-                               []byte{uint8(len(nald.Description))},
-                               nald.Description,
-                               nald.NewsArtList,
-                       ),
-               ),
-               io.EOF
+       buf := slices.Concat(
+               nald.ID[:],
+               count,
+               []byte{uint8(len(nald.Name))},
+               nald.Name,
+               []byte{uint8(len(nald.Description))},
+               nald.Description,
+               nald.NewsArtList,
+       )
+
+       if nald.readOffset >= len(buf) {
+               return 0, io.EOF // All bytes have been read
+       }
+       n := copy(p, buf[nald.readOffset:])
+       nald.readOffset += n
+
+       return n, nil
 }
 
 // NewsArtList is a summarized version of a NewArtData record for display in list view
 type NewsArtList struct {
-       ID          []byte // Size 4
-       TimeStamp   []byte // Year (2 bytes), milliseconds (2 bytes) and seconds (4 bytes)
-       ParentID    []byte // Size 4
-       Flags       []byte // Size 4
-       FlavorCount []byte // Size 2
+       ID          [4]byte
+       TimeStamp   [8]byte // Year (2 bytes), milliseconds (2 bytes) and seconds (4 bytes)
+       ParentID    [4]byte
+       Flags       [4]byte
+       FlavorCount [2]byte
        // Title size   1
        Title []byte // string
        // Poster size  1
@@ -150,21 +151,27 @@ func (s byID) Swap(i, j int) {
        s[i], s[j] = s[j], s[i]
 }
 func (s byID) Less(i, j int) bool {
-       return binary.BigEndian.Uint32(s[i].ID) < binary.BigEndian.Uint32(s[j].ID)
+       return binary.BigEndian.Uint32(s[i].ID[:]) < binary.BigEndian.Uint32(s[j].ID[:])
 }
 
+var (
+       NewsFlavorLen = []byte{0x0a}
+       NewsFlavor    = []byte("text/plain")
+)
+
 func (nal *NewsArtList) Read(p []byte) (int, error) {
        out := slices.Concat(
-               nal.ID,
-               nal.TimeStamp,
-               nal.ParentID,
-               nal.Flags,
-               []byte{0, 1},
+               nal.ID[:],
+               nal.TimeStamp[:],
+               nal.ParentID[:],
+               nal.Flags[:],
+               []byte{0, 1}, // Flavor Count
                []byte{uint8(len(nal.Title))},
                nal.Title,
                []byte{uint8(len(nal.Poster))},
                nal.Poster,
-               []byte{0x0a, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, 0x61, 0x69, 0x6e},
+               NewsFlavorLen,
+               NewsFlavor,
                nal.ArticleSize,
        )
 
@@ -190,17 +197,11 @@ func (newscat *NewsCategoryListData15) MarshalBinary() (data []byte, err error)
 
        out := append(newscat.Type[:], count...)
 
+       // If type is category
        if bytes.Equal(newscat.Type[:], []byte{0, 3}) {
-               // Generate a random GUID // TODO: does this need to be random?
-               b := make([]byte, 16)
-               _, err := rand.Read(b)
-               if err != nil {
-                       return data, err
-               }
-
-               out = append(out, b...)                  // GUID
-               out = append(out, []byte{0, 0, 0, 1}...) // Add SN (TODO: not sure what this is)
-               out = append(out, []byte{0, 0, 0, 2}...) // Delete SN (TODO: not sure what this is)
+               out = append(out, newscat.GUID[:]...)     // GUID
+               out = append(out, newscat.AddSN[:]...)    // Add SN
+               out = append(out, newscat.DeleteSN[:]...) // Delete SN
        }
 
        out = append(out, newscat.nameLen()...)
index 44776dd3723a0eee0f5631be3d594d1fb19f306e..a2102b23c79959147d0446252857fffcc2de0c5d 100644 (file)
@@ -12,9 +12,9 @@ func TestNewsCategoryListData15_MarshalBinary(t *testing.T) {
                Articles map[uint32]*NewsArtData
                SubCats  map[string]NewsCategoryListData15
                Count    []byte
-               AddSN    []byte
-               DeleteSN []byte
-               GUID     []byte
+               AddSN    [4]byte
+               DeleteSN [4]byte
+               GUID     [16]byte
        }
        tests := []struct {
                name     string
@@ -75,7 +75,6 @@ func TestNewsCategoryListData15_MarshalBinary(t *testing.T) {
                                Name:     tt.fields.Name,
                                Articles: tt.fields.Articles,
                                SubCats:  tt.fields.SubCats,
-                               Count:    tt.fields.Count,
                                AddSN:    tt.fields.AddSN,
                                DeleteSN: tt.fields.DeleteSN,
                                GUID:     tt.fields.GUID,
index ff8daf3fb3ccaff5f882c77bca28ae3f5399ee3c..448aab126791a5e2530c547f6154f5329fd4c02f 100644 (file)
@@ -246,7 +246,7 @@ func NewServer(configDir, netInterface string, netPort int, logger *slog.Logger,
        _ = server.loadBanList(filepath.Join(configDir, "Banlist.yaml"))
 
        if err := server.loadThreadedNews(filepath.Join(configDir, "ThreadedNews.yaml")); err != nil {
-               return nil, err
+               return nil, fmt.Errorf("error loading threaded news: %w", err)
        }
 
        if err := server.loadConfig(filepath.Join(configDir, "config.yaml")); err != nil {
index 7883bfbcb3c79b28315ce5566b99ec274219bf65..39dcd81f0c97dec02a8eb3425e491855706f19f3 100644 (file)
@@ -214,7 +214,8 @@ func (t *Transaction) Read(p []byte) (int, error) {
        bbuf := new(bytes.Buffer)
 
        for _, field := range t.Fields {
-               _, err := bbuf.ReadFrom(&field)
+               f := field
+               _, err := bbuf.ReadFrom(&f)
                if err != nil {
                        return 0, fmt.Errorf("error reading field: %w", err)
                }
index 1a079f74acb8f5b44e2f080cf1519065ec4f06a7..2d4eabdbf1cc478d5cf23e69fa027af6125e7d2d 100644 (file)
@@ -749,7 +749,8 @@ func HandleListUsers(cc *ClientConn, t *Transaction) (res []Transaction, err err
 
        var userFields []Field
        for _, acc := range cc.Server.Accounts {
-               b, err := io.ReadAll(acc)
+               accCopy := *acc
+               b, err := io.ReadAll(&accCopy)
                if err != nil {
                        return res, err
                }
@@ -1432,6 +1433,9 @@ func HandlePostNewsArt(cc *ClientConn, t *Transaction) (res []Transaction, err e
        bs := make([]byte, 4)
        binary.BigEndian.PutUint32(bs, convertedArtID)
 
+       cc.Server.mux.Lock()
+       defer cc.Server.mux.Unlock()
+
        newArt := NewsArtData{
                Title:         string(t.GetField(FieldNewsArtTitle).Data),
                Poster:        string(cc.UserName),
@@ -2053,19 +2057,11 @@ func HandleMakeAlias(cc *ClientConn, t *Transaction) (res []Transaction, err err
 // 107 FieldRefNum                     Used later for transfer
 // 108 FieldTransferSize       Size of data to be downloaded
 func HandleDownloadBanner(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
-       fi, err := cc.Server.FS.Stat(filepath.Join(cc.Server.ConfigDir, cc.Server.Config.BannerFile))
-       if err != nil {
-               return res, err
-       }
-
        ft := cc.newFileTransfer(bannerDownload, []byte{}, []byte{}, make([]byte, 4))
+       binary.BigEndian.PutUint32(ft.TransferSize, uint32(len(cc.Server.banner)))
 
-       binary.BigEndian.PutUint32(ft.TransferSize, uint32(fi.Size()))
-
-       res = append(res, cc.NewReply(t,
+       return append(res, cc.NewReply(t,
                NewField(FieldRefNum, ft.refNum[:]),
                NewField(FieldTransferSize, ft.TransferSize),
-       ))
-
-       return res, err
+       )), err
 }
index 2420649522712bbb5029056eb119af1470bf58c4..9c0359f14cc1af2d520dddc04df11b6fb6ad536a 100644 (file)
@@ -3191,10 +3191,8 @@ func TestHandleDelNewsItem(t *testing.T) {
                                        Server: &Server{
                                                ThreadedNews: &ThreadedNews{Categories: map[string]NewsCategoryListData15{
                                                        "test": {
-                                                               Type:     [2]byte{0, 3},
-                                                               Count:    nil,
-                                                               NameSize: 0,
-                                                               Name:     "zz",
+                                                               Type: [2]byte{0, 3},
+                                                               Name: "zz",
                                                        },
                                                }},
                                        },
@@ -3237,10 +3235,8 @@ func TestHandleDelNewsItem(t *testing.T) {
                                        Server: &Server{
                                                ThreadedNews: &ThreadedNews{Categories: map[string]NewsCategoryListData15{
                                                        "testcat": {
-                                                               Type:     [2]byte{0, 2},
-                                                               Count:    nil,
-                                                               NameSize: 0,
-                                                               Name:     "test",
+                                                               Type: [2]byte{0, 2},
+                                                               Name: "test",
                                                        },
                                                }},
                                        },
@@ -3293,10 +3289,8 @@ func TestHandleDelNewsItem(t *testing.T) {
                                                }(),
                                                ThreadedNews: &ThreadedNews{Categories: map[string]NewsCategoryListData15{
                                                        "testcat": {
-                                                               Type:     [2]byte{0, 2},
-                                                               Count:    nil,
-                                                               NameSize: 0,
-                                                               Name:     "test",
+                                                               Type: [2]byte{0, 2},
+                                                               Name: "test",
                                                        },
                                                }},
                                        },
@@ -3781,11 +3775,9 @@ func TestHandleNewNewsFldr(t *testing.T) {
                                                }(),
                                                ThreadedNews: &ThreadedNews{Categories: map[string]NewsCategoryListData15{
                                                        "test": {
-                                                               Type:     [2]byte{0, 2},
-                                                               Count:    nil,
-                                                               NameSize: 0,
-                                                               Name:     "test",
-                                                               SubCats:  make(map[string]NewsCategoryListData15),
+                                                               Type:    [2]byte{0, 2},
+                                                               Name:    "test",
+                                                               SubCats: make(map[string]NewsCategoryListData15),
                                                        },
                                                }},
                                        },
@@ -3885,3 +3877,27 @@ func TestHandleNewNewsFldr(t *testing.T) {
                })
        }
 }
+
+func TestHandleDownloadBanner(t *testing.T) {
+       type args struct {
+               cc *ClientConn
+               t  *Transaction
+       }
+       tests := []struct {
+               name    string
+               args    args
+               wantRes []Transaction
+               wantErr assert.ErrorAssertionFunc
+       }{
+               // TODO: Add test cases.
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       gotRes, err := HandleDownloadBanner(tt.args.cc, tt.args.t)
+                       if !tt.wantErr(t, err, fmt.Sprintf("HandleDownloadBanner(%v, %v)", tt.args.cc, tt.args.t)) {
+                               return
+                       }
+                       assert.Equalf(t, tt.wantRes, gotRes, "HandleDownloadBanner(%v, %v)", tt.args.cc, tt.args.t)
+               })
+       }
+}