}
var nostalgiaVersion = []byte{0, 0, 2, 0x2c} // version ID used by the Nostalgia client
+var frogblastVersion = []byte{0, 0, 0, 0xb9} // version ID used by the Frogblast 1.2.4 client
type Server struct {
Port int
}
// Used simplified hotline v1.2.3 login flow for clients that do not send login info in tranAgreed
- if c.Version == nil || bytes.Equal(c.Version, nostalgiaVersion) {
+ if c.Version == nil || bytes.Equal(c.Version, nostalgiaVersion) || bytes.Equal(c.Version, frogblastVersion) {
c.Agreed = true
c.logger = c.logger.With("name", string(c.UserName))
c.logger.Infow("Login successful", "clientVersion", fmt.Sprintf("%v", func() int { i, _ := byteToInt(c.Version); return i }()))
formattedMsg = fmt.Sprintf("\r*** %s %s", cc.UserName, t.GetField(fieldData).Data)
}
+ // The ChatID field is used to identify messages as belonging to a private chat.
+ // All clients *except* Frogblast omit this field for public chat, but Frogblast sends a value of 00 00 00 00.
chatID := t.GetField(fieldChatID).Data
- // a non-nil chatID indicates the message belongs to a private chat
- if chatID != nil {
+ if chatID != nil && !bytes.Equal([]byte{0, 0, 0, 0}, chatID) {
chatInt := binary.BigEndian.Uint32(chatID)
privChat := cc.Server.PrivateChats[chatInt]
},
wantErr: false,
},
+ {
+ name: "treats Chat ID 00 00 00 00 as a public chat message",
+ args: args{
+ cc: &ClientConn{
+ Account: &Account{
+ Access: func() accessBitmap {
+ var bits accessBitmap
+ bits.Set(accessSendChat)
+ return bits
+ }(),
+ },
+ UserName: []byte{0x00, 0x01},
+ Server: &Server{
+ Clients: map[uint16]*ClientConn{
+ uint16(1): {
+ Account: &Account{
+ Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
+ },
+ ID: &[]byte{0, 1},
+ },
+ uint16(2): {
+ Account: &Account{
+ Access: accessBitmap{255, 255, 255, 255, 255, 255, 255, 255},
+ },
+ ID: &[]byte{0, 2},
+ },
+ },
+ },
+ },
+ t: &Transaction{
+ Fields: []Field{
+ NewField(fieldData, []byte("hai")),
+ NewField(fieldChatID, []byte{0, 0, 0, 0}),
+ },
+ },
+ },
+ want: []Transaction{
+ {
+ clientID: &[]byte{0, 1},
+ Flags: 0x00,
+ IsReply: 0x00,
+ Type: []byte{0, 0x6a},
+ ID: []byte{0x9a, 0xcb, 0x04, 0x42}, // Random ID from rand.Seed(1)
+ ErrorCode: []byte{0, 0, 0, 0},
+ Fields: []Field{
+ NewField(fieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
+ },
+ },
+ {
+ clientID: &[]byte{0, 2},
+ Flags: 0x00,
+ IsReply: 0x00,
+ Type: []byte{0, 0x6a},
+ ID: []byte{0xf0, 0xc5, 0x34, 0x1e}, // Random ID from rand.Seed(1)
+ ErrorCode: []byte{0, 0, 0, 0},
+ Fields: []Field{
+ NewField(fieldData, []byte{0x0d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x01, 0x3a, 0x20, 0x20, 0x68, 0x61, 0x69}),
+ },
+ },
+ },
+ wantErr: false,
+ },
{
name: "when user does not have required permission",
args: args{