aboutsummaryrefslogtreecommitdiff
path: root/hotline
diff options
context:
space:
mode:
authorJeff Halter <868228+jhalter@users.noreply.github.com>2022-07-03 19:11:29 -0700
committerJeff Halter <868228+jhalter@users.noreply.github.com>2022-07-03 19:11:29 -0700
commitc1c44744fdbd3ddeec509efffba91011a4d49990 (patch)
treeecb4ebf0f0e33c56bc83bfd67c2143bf52a45924 /hotline
parent69af8ddbdff8e6d0dd551e9bdc72284469a86fc0 (diff)
Implement handing for "Refuse Private Chat" preference
Diffstat (limited to 'hotline')
-rw-r--r--hotline/server.go16
-rw-r--r--hotline/transaction_handlers.go38
-rw-r--r--hotline/transaction_handlers_test.go179
3 files changed, 217 insertions, 16 deletions
diff --git a/hotline/server.go b/hotline/server.go
index 852b772..06a0695 100644
--- a/hotline/server.go
+++ b/hotline/server.go
@@ -48,10 +48,13 @@ type Server struct {
Clients map[uint16]*ClientConn
fileTransfers map[[4]byte]*FileTransfer
- Config *Config
- ConfigDir string
- Logger *zap.SugaredLogger
- PrivateChats map[uint32]*PrivateChat
+ Config *Config
+ ConfigDir string
+ Logger *zap.SugaredLogger
+
+ PrivateChatsMu sync.Mutex
+ PrivateChats map[uint32]*PrivateChat
+
NextGuestID *uint16
TrackerPassID [4]byte
Stats *Stats
@@ -698,15 +701,14 @@ func (s *Server) handleNewConnection(ctx context.Context, rwc io.ReadWriteCloser
}
func (s *Server) NewPrivateChat(cc *ClientConn) []byte {
- s.mux.Lock()
- defer s.mux.Unlock()
+ s.PrivateChatsMu.Lock()
+ defer s.PrivateChatsMu.Unlock()
randID := make([]byte, 4)
rand.Read(randID)
data := binary.BigEndian.Uint32(randID[:])
s.PrivateChats[data] = &PrivateChat{
- Subject: "",
ClientConn: make(map[uint16]*ClientConn),
}
s.PrivateChats[data].ClientConn[cc.uint16ID()] = cc
diff --git a/hotline/transaction_handlers.go b/hotline/transaction_handlers.go
index afc24ff..2dc5e06 100644
--- a/hotline/transaction_handlers.go
+++ b/hotline/transaction_handlers.go
@@ -1704,15 +1704,35 @@ func HandleInviteNewChat(cc *ClientConn, t *Transaction) (res []Transaction, err
targetID := t.GetField(fieldUserID).Data
newChatID := cc.Server.NewPrivateChat(cc)
- res = append(res,
- *NewTransaction(
- tranInviteToChat,
- &targetID,
- NewField(fieldChatID, newChatID),
- NewField(fieldUserName, cc.UserName),
- NewField(fieldUserID, *cc.ID),
- ),
- )
+ // Halcyon does not accept private chats.
+
+ // Check if target user has "Refuse private chat" flag
+ binary.BigEndian.Uint16(targetID)
+ targetClient := cc.Server.Clients[binary.BigEndian.Uint16(targetID)]
+
+ flagBitmap := big.NewInt(int64(binary.BigEndian.Uint16(targetClient.Flags)))
+ if flagBitmap.Bit(userFLagRefusePChat) == 1 {
+ res = append(res,
+ *NewTransaction(
+ tranServerMsg,
+ cc.ID,
+ NewField(fieldData, []byte(string(targetClient.UserName)+" does not accept private messages.")),
+ NewField(fieldUserName, targetClient.UserName),
+ NewField(fieldUserID, *targetClient.ID),
+ NewField(fieldOptions, []byte{0, 2}),
+ ),
+ )
+ } else {
+ res = append(res,
+ *NewTransaction(
+ tranInviteToChat,
+ &targetID,
+ NewField(fieldChatID, newChatID),
+ NewField(fieldUserName, cc.UserName),
+ NewField(fieldUserID, *cc.ID),
+ ),
+ )
+ }
res = append(res,
cc.NewReply(t,
diff --git a/hotline/transaction_handlers_test.go b/hotline/transaction_handlers_test.go
index 0a95f62..cb9e21f 100644
--- a/hotline/transaction_handlers_test.go
+++ b/hotline/transaction_handlers_test.go
@@ -3286,3 +3286,182 @@ func TestHandleTranOldPostNews(t *testing.T) {
})
}
}
+
+func TestHandleInviteNewChat(t *testing.T) {
+ type args struct {
+ cc *ClientConn
+ t *Transaction
+ }
+ tests := []struct {
+ name string
+ args args
+ wantRes []Transaction
+ wantErr assert.ErrorAssertionFunc
+ }{
+ {
+ name: "when user does not have required permission",
+ args: args{
+ cc: &ClientConn{
+ Account: &Account{
+ Access: func() accessBitmap {
+ var bits accessBitmap
+ return bits
+ }(),
+ },
+ },
+ t: NewTransaction(tranInviteNewChat, &[]byte{0, 1}),
+ },
+ wantRes: []Transaction{
+ {
+ Flags: 0x00,
+ IsReply: 0x01,
+ Type: []byte{0, 0x00},
+ ID: []byte{0, 0, 0, 0},
+ ErrorCode: []byte{0, 0, 0, 1},
+ Fields: []Field{
+ NewField(fieldError, []byte("You are not allowed to request private chat.")),
+ },
+ },
+ },
+ wantErr: assert.NoError,
+ },
+ {
+ name: "when userA invites userB to new private chat",
+ args: args{
+ cc: &ClientConn{
+ ID: &[]byte{0, 1},
+ Account: &Account{
+ Access: func() accessBitmap {
+ var bits accessBitmap
+ bits.Set(accessOpenChat)
+ return bits
+ }(),
+ },
+ UserName: []byte("UserA"),
+ Icon: []byte{0, 1},
+ Flags: []byte{0, 0},
+ Server: &Server{
+ Clients: map[uint16]*ClientConn{
+ uint16(2): {
+ ID: &[]byte{0, 2},
+ UserName: []byte("UserB"),
+ Flags: []byte{0, 0},
+ },
+ },
+ PrivateChats: make(map[uint32]*PrivateChat),
+ },
+ },
+ t: NewTransaction(
+ tranInviteNewChat, &[]byte{0, 1},
+ NewField(fieldUserID, []byte{0, 2}),
+ ),
+ },
+ wantRes: []Transaction{
+ {
+ clientID: &[]byte{0, 2},
+ Flags: 0x00,
+ IsReply: 0x00,
+ Type: []byte{0, 0x71},
+ ID: []byte{0, 0, 0, 0},
+ ErrorCode: []byte{0, 0, 0, 0},
+ Fields: []Field{
+ NewField(fieldChatID, []byte{0x52, 0xfd, 0xfc, 0x07}),
+ NewField(fieldUserName, []byte("UserA")),
+ NewField(fieldUserID, []byte{0, 1}),
+ },
+ },
+
+ {
+ clientID: &[]byte{0, 1},
+ Flags: 0x00,
+ IsReply: 0x01,
+ Type: []byte{0, 0x70},
+ ID: []byte{0, 0, 0, 0},
+ ErrorCode: []byte{0, 0, 0, 0},
+ Fields: []Field{
+ NewField(fieldChatID, []byte{0x52, 0xfd, 0xfc, 0x07}),
+ NewField(fieldUserName, []byte("UserA")),
+ NewField(fieldUserID, []byte{0, 1}),
+ NewField(fieldUserIconID, []byte{0, 1}),
+ NewField(fieldUserFlags, []byte{0, 0}),
+ },
+ },
+ },
+ wantErr: assert.NoError,
+ },
+ {
+ name: "when userA invites userB to new private chat, but UserB has refuse private chat enabled",
+ args: args{
+ cc: &ClientConn{
+ ID: &[]byte{0, 1},
+ Account: &Account{
+ Access: func() accessBitmap {
+ var bits accessBitmap
+ bits.Set(accessOpenChat)
+ return bits
+ }(),
+ },
+ UserName: []byte("UserA"),
+ Icon: []byte{0, 1},
+ Flags: []byte{0, 0},
+ Server: &Server{
+ Clients: map[uint16]*ClientConn{
+ uint16(2): {
+ ID: &[]byte{0, 2},
+ UserName: []byte("UserB"),
+ Flags: []byte{255, 255},
+ },
+ },
+ PrivateChats: make(map[uint32]*PrivateChat),
+ },
+ },
+ t: NewTransaction(
+ tranInviteNewChat, &[]byte{0, 1},
+ NewField(fieldUserID, []byte{0, 2}),
+ ),
+ },
+ wantRes: []Transaction{
+ {
+ clientID: &[]byte{0, 1},
+ Flags: 0x00,
+ IsReply: 0x00,
+ Type: []byte{0, 0x68},
+ ID: []byte{0, 0, 0, 0},
+ ErrorCode: []byte{0, 0, 0, 0},
+ Fields: []Field{
+ NewField(fieldData, []byte("UserB does not accept private messages.")),
+ NewField(fieldUserName, []byte("UserB")),
+ NewField(fieldUserID, []byte{0, 2}),
+ NewField(fieldOptions, []byte{0, 2}),
+ },
+ },
+ {
+ clientID: &[]byte{0, 1},
+ Flags: 0x00,
+ IsReply: 0x01,
+ Type: []byte{0, 0x70},
+ ID: []byte{0, 0, 0, 0},
+ ErrorCode: []byte{0, 0, 0, 0},
+ Fields: []Field{
+ NewField(fieldChatID, []byte{0x52, 0xfd, 0xfc, 0x07}),
+ NewField(fieldUserName, []byte("UserA")),
+ NewField(fieldUserID, []byte{0, 1}),
+ NewField(fieldUserIconID, []byte{0, 1}),
+ NewField(fieldUserFlags, []byte{0, 0}),
+ },
+ },
+ },
+ wantErr: assert.NoError,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ rand.Seed(1)
+ gotRes, err := HandleInviteNewChat(tt.args.cc, tt.args.t)
+ if !tt.wantErr(t, err, fmt.Sprintf("HandleInviteNewChat(%v, %v)", tt.args.cc, tt.args.t)) {
+ return
+ }
+ tranAssertEqual(t, tt.wantRes, gotRes)
+ })
+ }
+}