Parcourir la source

fix blank IMs in ICQlite (v4+v5)

IMs were not sending because DirectConnect information was not
populating in the UserInfo struct. This commit populates DirectConnect
params via SetUserInfo at signon.
Mike il y a 2 mois
Parent
commit
0b5856415f
4 fichiers modifiés avec 103 ajouts et 1 suppressions
  1. 8 0
      foodgroup/oservice.go
  2. 47 0
      foodgroup/oservice_test.go
  3. 18 1
      state/session.go
  4. 30 0
      state/session_test.go

+ 8 - 0
foodgroup/oservice.go

@@ -258,6 +258,14 @@ func (s OServiceService) SetUserInfoFields(ctx context.Context, instance *state.
 		}
 	}
 
+	if dcBytes, hasDC := inBody.Bytes(wire.OServiceUserInfoICQDC); hasDC {
+		var dc wire.ICQDCInfo
+		if err := wire.UnmarshalBE(&dc, bytes.NewReader(dcBytes)); err != nil {
+			return wire.SNACMessage{}, err
+		}
+		instance.SetICQDCInfo(dc)
+	}
+
 	// reflect the status of this instance back to the caller, even though
 	// it does not reflect aggregated state of the session. this is necessary
 	// for the "invisible" button to properly toggle on the client.

+ 47 - 0
foodgroup/oservice_test.go

@@ -1,6 +1,7 @@
 package foodgroup
 
 import (
+	"bytes"
 	"context"
 	"io"
 	"log/slog"
@@ -1053,6 +1054,52 @@ func TestOServiceService_SetUserInfoFields(t *testing.T) {
 				assert.True(t, session.Invisible())
 			},
 		},
+		{
+			name:     "set ICQ direct connect info",
+			instance: newTestInstance("1000003", sessOptUserInfoFlag(wire.OServiceUserFlagICQ)),
+			inputSNAC: wire.SNACMessage{
+				Frame: wire.SNACFrame{
+					RequestID: 1234,
+				},
+				Body: wire.SNAC_0x01_0x1E_OServiceSetUserInfoFields{
+					TLVRestBlock: wire.TLVRestBlock{
+						TLVList: wire.TLVList{
+							wire.NewTLVBE(wire.OServiceUserInfoICQDC, wire.ICQDCInfo{
+								DCType:       4,
+								ProtoVersion: 10,
+							}),
+						},
+					},
+				},
+			},
+			expectOutput: wire.SNACMessage{
+				Frame: wire.SNACFrame{
+					FoodGroup: wire.OService,
+					SubGroup:  wire.OServiceUserInfoUpdate,
+					RequestID: 1234,
+				},
+				Body: func(val any) bool {
+					snac, ok := val.(wire.SNAC_0x01_0x0F_OServiceUserInfoUpdate)
+					if !ok || len(snac.UserInfo) == 0 {
+						return false
+					}
+					dc, hasDC := snac.UserInfo[0].Bytes(wire.OServiceUserInfoICQDC)
+					if !hasDC {
+						return false
+					}
+					var got wire.ICQDCInfo
+					if err := wire.UnmarshalBE(&got, bytes.NewReader(dc)); err != nil {
+						return false
+					}
+					return got.DCType == 4 && got.ProtoVersion == 10
+				},
+			},
+			checkSession: func(t *testing.T, session *state.Session) {
+				info := session.Instances()[0].ICQDCInfo()
+				assert.Equal(t, uint8(4), info.DCType)
+				assert.Equal(t, uint16(10), info.ProtoVersion)
+			},
+		},
 	}
 
 	for _, tc := range cases {

+ 18 - 1
state/session.go

@@ -844,7 +844,9 @@ func (s *Session) userInfo() wire.TLVList {
 	// ICQ direct-connect info. The TLV is required for buddy arrival events to
 	// work in ICQ, even if the values are set to default.
 	if baseUserFlags&wire.OServiceUserFlagICQ == wire.OServiceUserFlagICQ {
-		tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoICQDC, wire.ICQDCInfo{}))
+		if len(instances) > 0 {
+			tlvs.Append(wire.NewTLVBE(wire.OServiceUserInfoICQDC, instances[0].ICQDCInfo()))
+		}
 	}
 
 	caps := s.Caps()
@@ -891,6 +893,7 @@ type SessionInstance struct {
 	multiConnFlag     wire.MultiConnFlag
 	toc2              bool
 	toc2MsgEnc        bool
+	icqDCInfo         wire.ICQDCInfo
 
 	// Per-session state
 	idle              bool
@@ -1320,6 +1323,20 @@ func (s *SessionInstance) UserStatusBitmask() uint32 {
 	return s.userStatusBitmask
 }
 
+// SetICQDCInfo stores ICQ direct-connect settings from TLV 0x0C on SNAC(01,1E).
+func (s *SessionInstance) SetICQDCInfo(info wire.ICQDCInfo) {
+	s.mutex.Lock()
+	defer s.mutex.Unlock()
+	s.icqDCInfo = info
+}
+
+// ICQDCInfo returns stored ICQ direct-connect settings.
+func (s *SessionInstance) ICQDCInfo() wire.ICQDCInfo {
+	s.mutex.RLock()
+	defer s.mutex.RUnlock()
+	return s.icqDCInfo
+}
+
 // caps retrieves instance capabilities.
 func (s *SessionInstance) caps() [][16]byte {
 	s.mutex.RLock()

+ 30 - 0
state/session_test.go

@@ -164,6 +164,36 @@ func TestSession_TLVUserInfo(t *testing.T) {
 				},
 			},
 		},
+		{
+			name: "user is on ICQ with direct connect info from client",
+			givenSessionFn: func() *SessionInstance {
+				s := NewSession().AddInstance()
+				s.Session().SetSignonTime(time.Unix(1, 0))
+				s.Session().SetIdentScreenName(NewIdentScreenName("1000003"))
+				s.Session().SetDisplayScreenName("1000003")
+				s.SetUserInfoFlag(wire.OServiceUserFlagICQ)
+				s.SetICQDCInfo(wire.ICQDCInfo{
+					DCType:       4,
+					ProtoVersion: 10,
+				})
+				return s
+			},
+			want: wire.TLVUserInfo{
+				ScreenName: "1000003",
+				TLVBlock: wire.TLVBlock{
+					TLVList: wire.TLVList{
+						wire.NewTLVBE(wire.OServiceUserInfoSignonTOD, uint32(1)),
+						wire.NewTLVBE(wire.OServiceUserInfoUserFlags, wire.OServiceUserFlagOSCARFree|wire.OServiceUserFlagICQ),
+						wire.NewTLVBE(wire.OServiceUserInfoStatus, uint32(0x0000)),
+						wire.NewTLVBE(wire.OServiceUserInfoICQDC, wire.ICQDCInfo{
+							DCType:       4,
+							ProtoVersion: 10,
+						}),
+						wire.NewTLVBE(wire.OServiceUserInfoMySubscriptions, uint32(0)),
+					},
+				},
+			},
+		},
 		{
 			name: "user has away message set - all instances away",
 			givenSessionFn: func() *SessionInstance {