Quellcode durchsuchen

icqlegacy: fix presence at login

The user buddy list is registered only when the client sends its
contacts. Buddy list registration is required so that the user can
be visibile to other users. If the client never sends its contacts,
the buddy list is never registered.

This commit registers the buddy list at signon, bringing its behavior
in line with the other servers.
Mike vor 1 Monat
Ursprung
Commit
05ea138c3d

+ 1 - 1
server/icq_legacy/handler.go

@@ -381,7 +381,7 @@ type LegacyService interface {
 	// NotifyUserOffline broadcasts a user departure to OSCAR clients.
 	// NotifyUserOffline broadcasts a user departure to OSCAR clients.
 	NotifyUserOffline(ctx context.Context, uin uint32) error
 	NotifyUserOffline(ctx context.Context, uin uint32) error
 
 
-	// NotifyUserOnline broadcasts a user arrival to OSCAR clients.
+	// NotifyUserOnline broadcasts a user arrival to OSCAR clients after legacy login.
 	NotifyUserOnline(ctx context.Context, uin uint32, status uint32) error
 	NotifyUserOnline(ctx context.Context, uin uint32, status uint32) error
 
 
 	// User Management
 	// User Management

+ 2 - 8
server/icq_legacy/v2_handler.go

@@ -243,9 +243,8 @@ func (h *V2Handler) handleLogin(session *LegacySession, addr *net.UDPAddr, pkt *
 		"session_id", newSession.SessionID,
 		"session_id", newSession.SessionID,
 	)
 	)
 
 
-	// 6. Notify contacts that user is online (via service layer)
-	if err := h.service.NotifyStatusChange(ctx, pkt.UIN, loginData.Status); err != nil {
-		h.logger.Debug("failed to notify status change", "err", err)
+	if err := h.service.NotifyUserOnline(ctx, newSession.UIN, newSession.GetStatus()); err != nil {
+		h.logger.Debug("V2 failed to notify OSCAR clients of online", "uin", newSession.UIN, "err", err)
 	}
 	}
 
 
 	return nil
 	return nil
@@ -362,11 +361,6 @@ func (h *V2Handler) handleContactList(session *LegacySession, pkt *V2ClientPacke
 		}
 		}
 	}
 	}
 
 
-	// Notify OSCAR clients that this legacy user is online
-	if err := h.service.NotifyUserOnline(ctx, session.UIN, session.GetStatus()); err != nil {
-		h.logger.Debug("V2 failed to notify OSCAR clients of online", "uin", session.UIN, "err", err)
-	}
-
 	// 4. Send contact list done using packet builder
 	// 4. Send contact list done using packet builder
 	return h.sender.SendToSession(session, h.packetBuilder.BuildContactListDone(session.NextServerSeqNum(), session.UIN))
 	return h.sender.SendToSession(session, h.packetBuilder.BuildContactListDone(session.NextServerSeqNum(), session.UIN))
 }
 }

+ 1 - 2
server/icq_legacy/v2_handler_test.go

@@ -380,7 +380,7 @@ func TestV2Handler_HandleLogin(t *testing.T) {
 				e.Service.EXPECT().AuthenticateUser(mock.Anything, mock.MatchedBy(func(req AuthRequest) bool {
 				e.Service.EXPECT().AuthenticateUser(mock.Anything, mock.MatchedBy(func(req AuthRequest) bool {
 					return req.UIN == testV2UIN && req.Password == "secret"
 					return req.UIN == testV2UIN && req.Password == "secret"
 				})).Return(&AuthResult{Success: true, oscarSession: testAuthSuccessInstance()}, nil).Once()
 				})).Return(&AuthResult{Success: true, oscarSession: testAuthSuccessInstance()}, nil).Once()
-				e.Service.EXPECT().NotifyStatusChange(mock.Anything, testV2UIN, mock.Anything).Return(nil).Once()
+				e.Service.EXPECT().NotifyUserOnline(mock.Anything, testV2UIN, mock.Anything).Return(nil).Once()
 				e.Sender.EXPECT().SendToSession(mock.AnythingOfType("*icq_legacy.LegacySession"), mock.AnythingOfType("[]uint8")).Return(nil).Twice()
 				e.Sender.EXPECT().SendToSession(mock.AnythingOfType("*icq_legacy.LegacySession"), mock.AnythingOfType("[]uint8")).Return(nil).Twice()
 			}, func(e v2HandleEnv, err error) {
 			}, func(e v2HandleEnv, err error) {
 				assert.NoError(e.T, err)
 				assert.NoError(e.T, err)
@@ -423,7 +423,6 @@ func TestV2Handler_HandleContactList(t *testing.T) {
 			setup: v2CaseSetup(func(e v2HandleEnv) {
 			setup: v2CaseSetup(func(e v2HandleEnv) {
 				e.Service.EXPECT().ProcessContactList(mock.Anything, mock.Anything, mock.Anything).
 				e.Service.EXPECT().ProcessContactList(mock.Anything, mock.Anything, mock.Anything).
 					Return(&ContactListResult{}, nil).Once()
 					Return(&ContactListResult{}, nil).Once()
-				e.Service.EXPECT().NotifyUserOnline(mock.Anything, testV2UIN, mock.Anything).Return(nil).Once()
 				e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
 				e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
 				e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
 				e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
 					Run(func(_ *LegacySession, p []byte) {
 					Run(func(_ *LegacySession, p []byte) {

+ 4 - 7
server/icq_legacy/v3_handler.go

@@ -376,6 +376,10 @@ func (h *V3Handler) handleLogin(session *LegacySession, addr *net.UDPAddr, seq1,
 		"session_id", newSession.SessionID,
 		"session_id", newSession.SessionID,
 	)
 	)
 
 
+	if err := h.service.NotifyUserOnline(ctx, newSession.UIN, newSession.GetStatus()); err != nil {
+		h.logger.Debug("V3 failed to notify OSCAR clients of online", "uin", newSession.UIN, "err", err)
+	}
+
 	return nil
 	return nil
 }
 }
 
 
@@ -481,15 +485,8 @@ func (h *V3Handler) handleContactList(session *LegacySession, seq1, seq2 uint16,
 		}
 		}
 	}
 	}
 
 
-	// Also notify contacts that THIS user is now online
-	// This is the key fix - we need to tell contacts who have us in their list
 	h.notifyContactsUserOnline(session)
 	h.notifyContactsUserOnline(session)
 
 
-	// Notify OSCAR clients that this legacy user is online
-	if err := h.service.NotifyUserOnline(ctx, session.UIN, session.GetStatus()); err != nil {
-		h.logger.Debug("V3 failed to notify OSCAR clients of online", "uin", session.UIN, "err", err)
-	}
-
 	// 5. Send contact list done using packet builder
 	// 5. Send contact list done using packet builder
 	return h.sender.SendToSession(session, h.packetBuilder.BuildContactListDone(session.NextServerSeqNum(), seq2, session.UIN))
 	return h.sender.SendToSession(session, h.packetBuilder.BuildContactListDone(session.NextServerSeqNum(), seq2, session.UIN))
 }
 }

+ 1 - 1
server/icq_legacy/v3_handler_test.go

@@ -402,6 +402,7 @@ func TestV3Handler_HandleLogin(t *testing.T) {
 				e.Service.EXPECT().AuthenticateUser(mock.Anything, mock.MatchedBy(func(req AuthRequest) bool {
 				e.Service.EXPECT().AuthenticateUser(mock.Anything, mock.MatchedBy(func(req AuthRequest) bool {
 					return req.UIN == testV3UIN && req.Password == "secret"
 					return req.UIN == testV3UIN && req.Password == "secret"
 				})).Return(&AuthResult{Success: true, oscarSession: testAuthSuccessInstance()}, nil).Once()
 				})).Return(&AuthResult{Success: true, oscarSession: testAuthSuccessInstance()}, nil).Once()
+				e.Service.EXPECT().NotifyUserOnline(mock.Anything, testV3UIN, mock.Anything).Return(nil).Once()
 				e.Sender.EXPECT().SendToSession(mock.AnythingOfType("*icq_legacy.LegacySession"), mock.AnythingOfType("[]uint8")).Return(nil).Once()
 				e.Sender.EXPECT().SendToSession(mock.AnythingOfType("*icq_legacy.LegacySession"), mock.AnythingOfType("[]uint8")).Return(nil).Once()
 			}, func(e v3HandleEnv, err error) {
 			}, func(e v3HandleEnv, err error) {
 				assert.NoError(e.T, err)
 				assert.NoError(e.T, err)
@@ -446,7 +447,6 @@ func TestV3Handler_HandleContactList(t *testing.T) {
 			setup: v3CaseSetup(func(e v3HandleEnv) {
 			setup: v3CaseSetup(func(e v3HandleEnv) {
 				e.Service.EXPECT().ProcessContactList(mock.Anything, mock.Anything, mock.Anything).
 				e.Service.EXPECT().ProcessContactList(mock.Anything, mock.Anything, mock.Anything).
 					Return(&ContactListResult{}, nil).Once()
 					Return(&ContactListResult{}, nil).Once()
-				e.Service.EXPECT().NotifyUserOnline(mock.Anything, testV3UIN, mock.Anything).Return(nil).Once()
 				e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
 				e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
 				e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
 				e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).Return(nil).Once()
 			}, nil),
 			}, nil),

+ 6 - 8
server/icq_legacy/v4_handler.go

@@ -634,8 +634,10 @@ func (h *V4Handler) handleLogin(session *LegacySession, addr *net.UDPAddr, seq1,
 			"session_id", existingSession.SessionID,
 			"session_id", existingSession.SessionID,
 		)
 		)
 
 
-		// Notify other sessions that have this user in their contact list
 		h.notifyContactsUserOnline(existingSession)
 		h.notifyContactsUserOnline(existingSession)
+		if err := h.service.NotifyUserOnline(ctx, existingSession.UIN, existingSession.GetStatus()); err != nil {
+			h.logger.Debug("V4 failed to notify OSCAR clients of online", "uin", existingSession.UIN, "err", err)
+		}
 	} else {
 	} else {
 		// Direct login flow: client sent Login (0x03E8) without prior
 		// Direct login flow: client sent Login (0x03E8) without prior
 		// GetDeps. Just create session and send login reply directly.
 		// GetDeps. Just create session and send login reply directly.
@@ -659,8 +661,10 @@ func (h *V4Handler) handleLogin(session *LegacySession, addr *net.UDPAddr, seq1,
 			"session_id", newSession.SessionID,
 			"session_id", newSession.SessionID,
 		)
 		)
 
 
-		// Notify other sessions that have this user in their contact list
 		h.notifyContactsUserOnline(newSession)
 		h.notifyContactsUserOnline(newSession)
+		if err := h.service.NotifyUserOnline(ctx, newSession.UIN, newSession.GetStatus()); err != nil {
+			h.logger.Debug("V4 failed to notify OSCAR clients of online", "uin", newSession.UIN, "err", err)
+		}
 	}
 	}
 
 
 	return nil
 	return nil
@@ -776,14 +780,8 @@ func (h *V4Handler) handleContactList(session *LegacySession, seq1, seq2 uint16,
 		}
 		}
 	}
 	}
 
 
-	// Also notify contacts that THIS user is now online
 	h.notifyContactsUserOnline(session)
 	h.notifyContactsUserOnline(session)
 
 
-	// Notify OSCAR clients that this legacy user is online
-	if err := h.service.NotifyUserOnline(ctx, session.UIN, session.GetStatus()); err != nil {
-		h.logger.Debug("V4 failed to notify OSCAR clients of online", "uin", session.UIN, "err", err)
-	}
-
 	// 5. Send contact list done using packet builder
 	// 5. Send contact list done using packet builder
 	return h.sender.SendToSession(session, h.packetBuilder.BuildContactListDone(session.NextServerSeqNum(), seq2, session.UIN))
 	return h.sender.SendToSession(session, h.packetBuilder.BuildContactListDone(session.NextServerSeqNum(), seq2, session.UIN))
 }
 }

+ 1 - 1
server/icq_legacy/v4_handler_test.go

@@ -464,6 +464,7 @@ func TestV4Handler_HandleLogin(t *testing.T) {
 				e.Service.EXPECT().AuthenticateUser(mock.Anything, mock.MatchedBy(func(req AuthRequest) bool {
 				e.Service.EXPECT().AuthenticateUser(mock.Anything, mock.MatchedBy(func(req AuthRequest) bool {
 					return req.UIN == testV4UIN && req.Password == "secret" && req.Version == ICQLegacyVersionV4
 					return req.UIN == testV4UIN && req.Password == "secret" && req.Version == ICQLegacyVersionV4
 				})).Return(&AuthResult{Success: true, oscarSession: testAuthSuccessInstance()}, nil).Once()
 				})).Return(&AuthResult{Success: true, oscarSession: testAuthSuccessInstance()}, nil).Once()
+				e.Service.EXPECT().NotifyUserOnline(mock.Anything, testV4UIN, mock.Anything).Return(nil).Once()
 				e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
 				e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
 				e.Sender.EXPECT().SendToSession(mock.AnythingOfType("*icq_legacy.LegacySession"), mock.AnythingOfType("[]uint8")).Return(nil).Once()
 				e.Sender.EXPECT().SendToSession(mock.AnythingOfType("*icq_legacy.LegacySession"), mock.AnythingOfType("[]uint8")).Return(nil).Once()
 			}, func(e v4HandleEnv, err error) {
 			}, func(e v4HandleEnv, err error) {
@@ -509,7 +510,6 @@ func TestV4Handler_HandleContactList(t *testing.T) {
 			setup: v4CaseSetup(func(e v4HandleEnv) {
 			setup: v4CaseSetup(func(e v4HandleEnv) {
 				e.Service.EXPECT().ProcessContactList(mock.Anything, mock.Anything, mock.Anything).
 				e.Service.EXPECT().ProcessContactList(mock.Anything, mock.Anything, mock.Anything).
 					Return(&ContactListResult{}, nil).Once()
 					Return(&ContactListResult{}, nil).Once()
-				e.Service.EXPECT().NotifyUserOnline(mock.Anything, testV4UIN, mock.Anything).Return(nil).Once()
 				e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
 				e.Sender.EXPECT().SendPacket(testV4Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
 				e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
 				e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
 					Run(func(_ *LegacySession, p []byte) {
 					Run(func(_ *LegacySession, p []byte) {

+ 4 - 7
server/icq_legacy/v5_handler.go

@@ -489,15 +489,8 @@ func (h *V5Handler) handleContactList(session *LegacySession, pkt *V5ClientPacke
 		}
 		}
 	}
 	}
 
 
-	// Also notify contacts that THIS user is now online
-	// This is the key fix - we need to tell contacts who have us in their list
 	h.notifyContactsUserOnline(session)
 	h.notifyContactsUserOnline(session)
 
 
-	// Notify OSCAR clients that this legacy user is online
-	if err := h.service.NotifyUserOnline(ctx, session.UIN, session.GetStatus()); err != nil {
-		h.logger.Debug("V5 failed to notify OSCAR clients of online", "uin", session.UIN, "err", err)
-	}
-
 	// 5. Send contact list done using packet builder
 	// 5. Send contact list done using packet builder
 	return h.sender.SendToSession(session, h.packetBuilder.BuildContactListDone(session, pkt.SeqNum2))
 	return h.sender.SendToSession(session, h.packetBuilder.BuildContactListDone(session, pkt.SeqNum2))
 }
 }
@@ -1256,6 +1249,10 @@ func (h *V5Handler) handleLogin(session *LegacySession, addr *net.UDPAddr, pkt *
 		"session_id", newSession.SessionID,
 		"session_id", newSession.SessionID,
 	)
 	)
 
 
+	if err := h.service.NotifyUserOnline(ctx, newSession.UIN, newSession.GetStatus()); err != nil {
+		h.logger.Debug("V5 failed to notify OSCAR clients of online", "uin", newSession.UIN, "err", err)
+	}
+
 	return nil
 	return nil
 }
 }
 
 

+ 1 - 1
server/icq_legacy/v5_handler_test.go

@@ -593,6 +593,7 @@ func TestV5Handler_HandleLogin(t *testing.T) {
 					e.Service.EXPECT().AuthenticateUser(mock.Anything, mock.MatchedBy(func(req AuthRequest) bool {
 					e.Service.EXPECT().AuthenticateUser(mock.Anything, mock.MatchedBy(func(req AuthRequest) bool {
 						return req.UIN == testV5UIN && req.Password == "secret"
 						return req.UIN == testV5UIN && req.Password == "secret"
 					})).Return(&AuthResult{Success: true, oscarSession: testAuthSuccessInstance()}, nil).Once()
 					})).Return(&AuthResult{Success: true, oscarSession: testAuthSuccessInstance()}, nil).Once()
+					e.Service.EXPECT().NotifyUserOnline(mock.Anything, testV5UIN, mock.Anything).Return(nil).Once()
 					e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
 					e.Sender.EXPECT().SendPacket(testV5Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
 					e.Sender.EXPECT().SendToSession(mock.AnythingOfType("*icq_legacy.LegacySession"), mock.AnythingOfType("[]uint8")).
 					e.Sender.EXPECT().SendToSession(mock.AnythingOfType("*icq_legacy.LegacySession"), mock.AnythingOfType("[]uint8")).
 						Return(nil).Once()
 						Return(nil).Once()
@@ -685,7 +686,6 @@ func TestV5Handler_HandleContactList(t *testing.T) {
 				func(e v5HandleEnv) {
 				func(e v5HandleEnv) {
 					e.Service.EXPECT().ProcessContactList(mock.Anything, mock.Anything, mock.Anything).
 					e.Service.EXPECT().ProcessContactList(mock.Anything, mock.Anything, mock.Anything).
 						Return(&ContactListResult{}, nil).Once()
 						Return(&ContactListResult{}, nil).Once()
-					e.Service.EXPECT().NotifyUserOnline(mock.Anything, testV5UIN, mock.Anything).Return(nil).Once()
 					e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
 					e.Sender.EXPECT().SendPacket(e.Session.Addr, mock.AnythingOfType("[]uint8")).Return(nil).Once()
 					e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
 					e.Sender.EXPECT().SendToSession(e.Session, mock.AnythingOfType("[]uint8")).
 						Run(func(_ *LegacySession, p []byte) {
 						Run(func(_ *LegacySession, p []byte) {