Преглед изворни кода

toc2: add CHAT_IN_ENC response

Mike пре 4 месеци
родитељ
комит
c73509ef95
4 измењених фајлова са 225 додато и 4 уклоњено
  1. 10 0
      server/toc/cmd_client.go
  2. 179 0
      server/toc/cmd_client_test.go
  3. 13 3
      server/toc/cmd_server.go
  4. 23 1
      server/toc/cmd_server_test.go

+ 10 - 0
server/toc/cmd_client.go

@@ -540,6 +540,9 @@ func (s OSCARProxy) ChatAccept(
 	}
 	}
 
 
 	chatRegistry.RegisterSess(chatID, chatSess)
 	chatRegistry.RegisterSess(chatID, chatSess)
+	if me.IsTOC2() {
+		chatSess.SetTOC2(me.SupportsTOC2MsgEnc())
+	}
 
 
 	return chatID, []string{fmt.Sprintf("CHAT_JOIN:%d:%s", chatID, roomName)}
 	return chatID, []string{fmt.Sprintf("CHAT_JOIN:%d:%s", chatID, roomName)}
 }
 }
@@ -732,6 +735,9 @@ func (s OSCARProxy) ChatJoin(
 	}
 	}
 	chatID := chatRegistry.Add(roomInfo)
 	chatID := chatRegistry.Add(roomInfo)
 	chatRegistry.RegisterSess(chatID, chatSess)
 	chatRegistry.RegisterSess(chatID, chatSess)
+	if me.IsTOC2() {
+		chatSess.SetTOC2(me.SupportsTOC2MsgEnc())
+	}
 
 
 	return chatID, []string{fmt.Sprintf("CHAT_JOIN:%d:%s", chatID, roomName)}
 	return chatID, []string{fmt.Sprintf("CHAT_JOIN:%d:%s", chatID, roomName)}
 }
 }
@@ -847,6 +853,10 @@ func (s OSCARProxy) ChatSend(ctx context.Context, chatRegistry *ChatRegistry, ar
 			return s.runtimeErr(ctx, fmt.Errorf("wire.UnmarshalBE: %w", err))
 			return s.runtimeErr(ctx, fmt.Errorf("wire.UnmarshalBE: %w", err))
 		}
 		}
 
 
+		if me.SupportsTOC2MsgEnc() {
+			return []string{fmt.Sprintf("CHAT_IN_ENC:%d:%s:F:A:en:%s", chatID, userInfo.ScreenName, reflectMsg)}
+		}
+
 		return []string{fmt.Sprintf("CHAT_IN:%d:%s:F:%s", chatID, userInfo.ScreenName, reflectMsg)}
 		return []string{fmt.Sprintf("CHAT_IN:%d:%s:F:%s", chatID, userInfo.ScreenName, reflectMsg)}
 	default:
 	default:
 		return s.runtimeErr(ctx, errors.New("ChatService.ChannelMsgToHost: unexpected response"))
 		return s.runtimeErr(ctx, errors.New("ChatService.ChannelMsgToHost: unexpected response"))

+ 179 - 0
server/toc/cmd_client_test.go

@@ -595,6 +595,8 @@ func TestOSCARProxy_RecvClientCmd_ChatAccept(t *testing.T) {
 		// expectChatSession indicates whether a chat session should be present
 		// expectChatSession indicates whether a chat session should be present
 		// in the chat registry
 		// in the chat registry
 		expectChatSession bool
 		expectChatSession bool
+		// checkSession validates the state of the registered chat session (chat ID 0)
+		checkSession func(*testing.T, *state.SessionInstance)
 		// mockParams is the list of params sent to mocks that satisfy this
 		// mockParams is the list of params sent to mocks that satisfy this
 		// method's dependencies
 		// method's dependencies
 		mockParams mockParams
 		mockParams mockParams
@@ -658,6 +660,70 @@ func TestOSCARProxy_RecvClientCmd_ChatAccept(t *testing.T) {
 			wantMsg:           []string{"CHAT_JOIN:0:cool room"},
 			wantMsg:           []string{"CHAT_JOIN:0:cool room"},
 			expectChatSession: true,
 			expectChatSession: true,
 		},
 		},
+		{
+			name:     "accept chat - TOC2 with encoded messaging propagates to chat session",
+			me:       newTestSession("me", func(i *state.SessionInstance) { i.SetTOC2(true) }),
+			givenCmd: []byte(`toc_chat_accept 0`),
+			givenChatRegistry: func() *ChatRegistry {
+				reg := NewChatRegistry()
+				reg.Add(wire.ICBMRoomInfo{
+					Cookie:   "the-cookie",
+					Exchange: 4,
+					Instance: 0,
+				})
+				return reg
+			}(),
+			mockParams: mockParams{
+				chatNavParams: chatNavParams{
+					requestRoomInfoParams: requestRoomInfoParams{
+						{
+							inBody: wire.SNAC_0x0D_0x04_ChatNavRequestRoomInfo{
+								Cookie:         "the-cookie",
+								Exchange:       4,
+								InstanceNumber: 0,
+							},
+							msg: navInfo,
+						},
+					},
+				},
+				oServiceParams: oServiceParams{
+					serviceRequestParams: serviceRequestParams{
+						{
+							me:     state.NewIdentScreenName("me"),
+							bodyIn: svcReq,
+							msg:    svcResp,
+						},
+					},
+					clientOnlineParams: clientOnlineParams{
+						{
+							body: wire.SNAC_0x01_0x02_OServiceClientOnline{},
+							me:   state.NewIdentScreenName("me"),
+						},
+					},
+				},
+				authParams: authParams{
+					crackCookieParams: crackCookieParams{
+						{
+							cookieIn:  []byte("chat-auth-cookie"),
+							cookieOut: state.ServerCookie{ChatCookie: "chat-auth-cookie"},
+						},
+					},
+					registerChatSessionParams: registerChatSessionParams{
+						{
+							authCookie: state.ServerCookie{ChatCookie: "chat-auth-cookie"},
+							instance:   newTestSession("me"),
+						},
+					},
+				},
+			},
+			wantMsg:           []string{"CHAT_JOIN:0:cool room"},
+			expectChatSession: true,
+			checkSession: func(t *testing.T, sess *state.SessionInstance) {
+				assert.NotNil(t, sess, "chat session should be registered")
+				assert.True(t, sess.IsTOC2(), "chat session should have TOC2 set")
+				assert.True(t, sess.SupportsTOC2MsgEnc(), "chat session should have TOC2 msg enc set")
+			},
+		},
 		{
 		{
 			name:     "accept chat, receive error from client online",
 			name:     "accept chat, receive error from client online",
 			me:       newTestSession("me"),
 			me:       newTestSession("me"),
@@ -908,6 +974,9 @@ func TestOSCARProxy_RecvClientCmd_ChatAccept(t *testing.T) {
 			assert.NoError(t, g.Wait())
 			assert.NoError(t, g.Wait())
 			assert.Equal(t, tc.wantMsg, msg)
 			assert.Equal(t, tc.wantMsg, msg)
 			assert.Equal(t, tc.expectChatSession, len(tc.givenChatRegistry.Sessions()) == 1)
 			assert.Equal(t, tc.expectChatSession, len(tc.givenChatRegistry.Sessions()) == 1)
+			if tc.checkSession != nil {
+				tc.checkSession(t, tc.givenChatRegistry.RetrieveSess(0))
+			}
 		})
 		})
 	}
 	}
 }
 }
@@ -1124,6 +1193,8 @@ func TestOSCARProxy_RecvClientCmd_ChatJoin(t *testing.T) {
 		// expectChatSession indicates whether a chat session should be present
 		// expectChatSession indicates whether a chat session should be present
 		// in the chat registry
 		// in the chat registry
 		expectChatSession bool
 		expectChatSession bool
+		// checkSession validates the state of the registered chat session (chat ID 0)
+		checkSession func(*testing.T, *state.SessionInstance)
 		// mockParams is the list of params sent to mocks that satisfy this
 		// mockParams is the list of params sent to mocks that satisfy this
 		// method's dependencies
 		// method's dependencies
 		mockParams mockParams
 		mockParams mockParams
@@ -1176,6 +1247,59 @@ func TestOSCARProxy_RecvClientCmd_ChatJoin(t *testing.T) {
 			wantMsg:           []string{"CHAT_JOIN:0:cool room :)"},
 			wantMsg:           []string{"CHAT_JOIN:0:cool room :)"},
 			expectChatSession: true,
 			expectChatSession: true,
 		},
 		},
+		{
+			name:              "successfully join chat - TOC2 with encoded messaging propagates to chat session",
+			me:                newTestSession("me", func(i *state.SessionInstance) { i.SetTOC2(true) }),
+			givenCmd:          []byte(`toc_chat_join 4 "cool room :\)"`),
+			givenChatRegistry: NewChatRegistry(),
+			mockParams: mockParams{
+				chatNavParams: chatNavParams{
+					createRoomParams: createRoomParams{
+						{
+							me:     state.NewIdentScreenName("me"),
+							inBody: roomInfo,
+							msg:    navInfo,
+						},
+					},
+				},
+				oServiceParams: oServiceParams{
+					serviceRequestParams: serviceRequestParams{
+						{
+							me:     state.NewIdentScreenName("me"),
+							bodyIn: svcReq,
+							msg:    svcResp,
+						},
+					},
+					clientOnlineParams: clientOnlineParams{
+						{
+							body: wire.SNAC_0x01_0x02_OServiceClientOnline{},
+							me:   state.NewIdentScreenName("me"),
+						},
+					},
+				},
+				authParams: authParams{
+					crackCookieParams: crackCookieParams{
+						{
+							cookieIn:  []byte("chat-auth-cookie"),
+							cookieOut: state.ServerCookie{ChatCookie: "chat-auth-cookie"},
+						},
+					},
+					registerChatSessionParams: registerChatSessionParams{
+						{
+							authCookie: state.ServerCookie{ChatCookie: "chat-auth-cookie"},
+							instance:   newTestSession("me"),
+						},
+					},
+				},
+			},
+			wantMsg:           []string{"CHAT_JOIN:0:cool room :)"},
+			expectChatSession: true,
+			checkSession: func(t *testing.T, sess *state.SessionInstance) {
+				assert.NotNil(t, sess, "chat session should be registered")
+				assert.True(t, sess.IsTOC2(), "chat session should have TOC2 set")
+				assert.True(t, sess.SupportsTOC2MsgEnc(), "chat session should have TOC2 msg enc set")
+			},
+		},
 		{
 		{
 			name:              "accept chat, receive error from client online",
 			name:              "accept chat, receive error from client online",
 			me:                newTestSession("me"),
 			me:                newTestSession("me"),
@@ -1382,6 +1506,9 @@ func TestOSCARProxy_RecvClientCmd_ChatJoin(t *testing.T) {
 			assert.NoError(t, g.Wait())
 			assert.NoError(t, g.Wait())
 			assert.Equal(t, tc.wantMsg, msg)
 			assert.Equal(t, tc.wantMsg, msg)
 			assert.Equal(t, tc.expectChatSession, len(tc.givenChatRegistry.Sessions()) == 1)
 			assert.Equal(t, tc.expectChatSession, len(tc.givenChatRegistry.Sessions()) == 1)
+			if tc.checkSession != nil {
+				tc.checkSession(t, tc.givenChatRegistry.RetrieveSess(0))
+			}
 		})
 		})
 	}
 	}
 }
 }
@@ -1529,6 +1656,58 @@ func TestOSCARProxy_RecvClientCmd_ChatSend(t *testing.T) {
 			},
 			},
 			wantMsg: []string{"CHAT_IN:0:me:F:Hello world! :)"},
 			wantMsg: []string{"CHAT_IN:0:me:F:Hello world! :)"},
 		},
 		},
+		{
+			name:     "successfully send chat message - TOC2 encoded returns CHAT_IN_ENC",
+			me:       newTestSession("me"),
+			givenCmd: []byte(`toc_chat_send 0 "Hello world! :\)"`),
+			givenChatRegistry: func() *ChatRegistry {
+				reg := NewChatRegistry()
+				reg.RegisterSess(0, newTestSession("me", func(i *state.SessionInstance) { i.SetTOC2(true) }))
+				return reg
+			}(),
+			mockParams: mockParams{
+				chatParams: chatParams{
+					channelMsgToHostParamsChat: channelMsgToHostParamsChat{
+						{
+							sender: state.NewIdentScreenName("me"),
+							inBody: wire.SNAC_0x0E_0x05_ChatChannelMsgToHost{
+								Channel: wire.ICBMChannelMIME,
+								TLVRestBlock: wire.TLVRestBlock{
+									TLVList: wire.TLVList{
+										wire.NewTLVBE(wire.ChatTLVEnableReflectionFlag, uint8(1)),
+										wire.NewTLVBE(wire.ChatTLVSenderInformation, newTestSession("me").Session().TLVUserInfo()),
+										wire.NewTLVBE(wire.ChatTLVPublicWhisperFlag, []byte{}),
+										wire.NewTLVBE(wire.ChatTLVMessageInfo, wire.TLVRestBlock{
+											TLVList: wire.TLVList{
+												wire.NewTLVBE(wire.ChatTLVMessageInfoText, "Hello world! :)"),
+											},
+										}),
+									},
+								},
+							},
+							result: &wire.SNACMessage{
+								Body: wire.SNAC_0x0E_0x06_ChatChannelMsgToClient{
+									Channel: wire.ICBMChannelMIME,
+									TLVRestBlock: wire.TLVRestBlock{
+										TLVList: wire.TLVList{
+											wire.NewTLVBE(wire.ChatTLVSenderInformation,
+												newTestSession("me").Session().TLVUserInfo()),
+											wire.NewTLVBE(wire.ChatTLVPublicWhisperFlag, []byte{}),
+											wire.NewTLVBE(wire.ChatTLVMessageInfo, wire.TLVRestBlock{
+												TLVList: wire.TLVList{
+													wire.NewTLVBE(wire.ChatTLVMessageInfoText, "Hello world! :)"),
+												},
+											}),
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			wantMsg: []string{"CHAT_IN_ENC:0:me:F:A:en:Hello world! :)"},
+		},
 		{
 		{
 			name:     "send chat message, receive error from chat svc",
 			name:     "send chat message, receive error from chat svc",
 			me:       newTestSession("me"),
 			me:       newTestSession("me"),

+ 13 - 3
server/toc/cmd_server.go

@@ -89,7 +89,7 @@ func (s OSCARProxy) RecvChat(ctx context.Context, me *state.SessionInstance, cha
 			case wire.SNAC_0x0E_0x03_ChatUsersJoined:
 			case wire.SNAC_0x0E_0x03_ChatUsersJoined:
 				sendOrCancel(ctx, ch, s.ChatUpdateBuddyArrived(v, chatID))
 				sendOrCancel(ctx, ch, s.ChatUpdateBuddyArrived(v, chatID))
 			case wire.SNAC_0x0E_0x06_ChatChannelMsgToClient:
 			case wire.SNAC_0x0E_0x06_ChatChannelMsgToClient:
-				sendOrCancel(ctx, ch, s.ChatIn(ctx, v, chatID))
+				sendOrCancel(ctx, ch, s.ChatIn(ctx, me, v, chatID))
 			default:
 			default:
 				s.Logger.DebugContext(ctx, fmt.Sprintf("unsupported snac. foodgroup: %s subgroup: %s",
 				s.Logger.DebugContext(ctx, fmt.Sprintf("unsupported snac. foodgroup: %s subgroup: %s",
 					wire.FoodGroupName(snac.Frame.FoodGroup),
 					wire.FoodGroupName(snac.Frame.FoodGroup),
@@ -99,14 +99,20 @@ func (s OSCARProxy) RecvChat(ctx context.Context, me *state.SessionInstance, cha
 	}
 	}
 }
 }
 
 
-// ChatIn handles the CHAT_IN TOC command.
+// ChatIn handles the CHAT_IN and ENC_CHAT_IN TOC commands.
 //
 //
 // From the TiK documentation:
 // From the TiK documentation:
 //
 //
 //	A chat message was sent in a chat room.
 //	A chat message was sent in a chat room.
 //
 //
+// From the BlueTOC documentation:
+//
+//	This command received instead of CHAT_IN. It is similar to TOC 1.0 except there are a two new parameters.
+//	One of them is language; the other is unknown but is usually "A"
+//
 // Command syntax: CHAT_IN:<Chat Room Id>:<Source User>:<Whisper? T/F>:<Message>
 // Command syntax: CHAT_IN:<Chat Room Id>:<Source User>:<Whisper? T/F>:<Message>
-func (s OSCARProxy) ChatIn(ctx context.Context, snac wire.SNAC_0x0E_0x06_ChatChannelMsgToClient, chatID int) []string {
+// Command syntax: CHAT_IN_ENC:<chatroom id>:<user>:<whisper T/F>:<???>:en:<message>
+func (s OSCARProxy) ChatIn(ctx context.Context, me *state.SessionInstance, snac wire.SNAC_0x0E_0x06_ChatChannelMsgToClient, chatID int) []string {
 	b, ok := snac.Bytes(wire.ChatTLVSenderInformation)
 	b, ok := snac.Bytes(wire.ChatTLVSenderInformation)
 	if !ok {
 	if !ok {
 		return s.runtimeErr(ctx, errors.New("snac.Bytes: missing wire.ChatTLVSenderInformation"))
 		return s.runtimeErr(ctx, errors.New("snac.Bytes: missing wire.ChatTLVSenderInformation"))
@@ -128,6 +134,10 @@ func (s OSCARProxy) ChatIn(ctx context.Context, snac wire.SNAC_0x0E_0x06_ChatCha
 		return s.runtimeErr(ctx, fmt.Errorf("wire.UnmarshalChatMessageText: %w", err))
 		return s.runtimeErr(ctx, fmt.Errorf("wire.UnmarshalChatMessageText: %w", err))
 	}
 	}
 
 
+	if me.SupportsTOC2MsgEnc() {
+		return []string{fmt.Sprintf("CHAT_IN_ENC:%d:%s:F:A:en:%s", chatID, u.ScreenName, text)}
+	}
+
 	return []string{fmt.Sprintf("CHAT_IN:%d:%s:F:%s", chatID, u.ScreenName, text)}
 	return []string{fmt.Sprintf("CHAT_IN:%d:%s:F:%s", chatID, u.ScreenName, text)}
 }
 }
 
 

+ 23 - 1
server/toc/cmd_server_test.go

@@ -28,7 +28,7 @@ func TestOSCARProxy_RecvBOS_ChatIn(t *testing.T) {
 		wantCmd string
 		wantCmd string
 	}{
 	}{
 		{
 		{
-			name:   "send chat message",
+			name:   "send chat message - plain CHAT_IN",
 			me:     newTestSession("me"),
 			me:     newTestSession("me"),
 			chatID: 0,
 			chatID: 0,
 			givenMsg: wire.SNACMessage{
 			givenMsg: wire.SNACMessage{
@@ -49,6 +49,28 @@ func TestOSCARProxy_RecvBOS_ChatIn(t *testing.T) {
 			},
 			},
 			wantCmd: "CHAT_IN:0:them:F:<p>hello world!</p>",
 			wantCmd: "CHAT_IN:0:them:F:<p>hello world!</p>",
 		},
 		},
+		{
+			name:   "send chat message - TOC2 encoded CHAT_IN_ENC",
+			me:     newTestSession("me", func(i *state.SessionInstance) { i.SetTOC2(true) }),
+			chatID: 0,
+			givenMsg: wire.SNACMessage{
+				Body: wire.SNAC_0x0E_0x06_ChatChannelMsgToClient{
+					TLVRestBlock: wire.TLVRestBlock{
+						TLVList: wire.TLVList{
+							wire.NewTLVBE(wire.ChatTLVSenderInformation, wire.TLVUserInfo{
+								ScreenName: "them",
+							}),
+							wire.NewTLVBE(wire.ChatTLVMessageInfo, wire.TLVRestBlock{
+								TLVList: wire.TLVList{
+									wire.NewTLVBE(wire.ChatTLVMessageInfoText, "<p>hello world!</p>"),
+								},
+							}),
+						},
+					},
+				},
+			},
+			wantCmd: "CHAT_IN_ENC:0:them:F:A:en:<p>hello world!</p>",
+		},
 	}
 	}
 
 
 	for _, tc := range cases {
 	for _, tc := range cases {