Skip to content

Commit

Permalink
move buddy list population from OService.ClientOnline to Feedbag.Use
Browse files Browse the repository at this point in the history
This is the first step towards supporting both client-side and
server-side buddy list management. Prior to this commit, the buddy list
is populated using the feedbag mechanism in OService.ClientOnline. This
logic is redundant and can cause confusion for older AIM clients that
use client-side buddy list management. Instead, we now populate the
buddy list using feedbag only when a feedbag-enabled client tells to via
the Feedbag.Use SNAC.
  • Loading branch information
mk6i committed Apr 21, 2024
1 parent 93b2b81 commit cc82a27
Show file tree
Hide file tree
Showing 8 changed files with 249 additions and 85 deletions.
20 changes: 20 additions & 0 deletions foodgroup/feedbag.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,3 +337,23 @@ func (s FeedbagService) DeleteItem(ctx context.Context, sess *state.Session, inF
// correctly unmarshalled.
func (s FeedbagService) StartCluster(context.Context, wire.SNACFrame, wire.SNAC_0x13_0x11_FeedbagStartCluster) {
}

// Use sends a user the contents of their buddy list. It's invoked at sign-on
// by AIM clients that use the feedbag food group for buddy list management (as
// opposed to client-side management).
func (s FeedbagService) Use(ctx context.Context, sess *state.Session) error {
buddies, err := s.feedbagManager.Buddies(sess.ScreenName())
if err != nil {
return err
}
for _, screenName := range buddies {
buddy := s.messageRelayer.RetrieveByScreenName(screenName)
if buddy == nil || buddy.Invisible() {
continue
}
if err := unicastArrival(ctx, buddy, sess, s.messageRelayer, s.feedbagManager); err != nil {
return err
}
}
return nil
}
117 changes: 117 additions & 0 deletions foodgroup/feedbag_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1495,3 +1495,120 @@ func TestFeedbagService_DeleteItem(t *testing.T) {
})
}
}

func TestFeedbagService_Use(t *testing.T) {
tests := []struct {
// name is the name of the test
name string
// joiningChatter is the session of the arriving user
sess *state.Session
// bodyIn is the SNAC body sent from the arriving user's client to the
// server
bodyIn wire.SNAC_0x01_0x02_OServiceClientOnline
// buddiesParams contains params for looking up arriving user's
// buddies
buddiesParams buddiesParams
// retrieveByScreenNameParams contains params for looking up the
// session for each of the arriving user's buddies
retrieveByScreenNameParams retrieveByScreenNameParams
// relayToScreenNameParams contains params for sending arrival
// notifications for each of the arriving user's buddies to the
// arriving user's client
relayToScreenNameParams relayToScreenNameParams
// feedbagParams contains params for retrieving a user's feedbag
feedbagParams feedbagParams
wantErr error
}{
{
name: "notify arriving user's buddies of its arrival and populate the arriving user's buddy list",
sess: newTestSession("test-user"),
bodyIn: wire.SNAC_0x01_0x02_OServiceClientOnline{},
buddiesParams: buddiesParams{
{
screenName: "test-user",
results: []string{"buddy1", "buddy3"},
},
},
retrieveByScreenNameParams: retrieveByScreenNameParams{
{
screenName: "buddy1",
sess: newTestSession("buddy1"),
},
{
screenName: "buddy3",
sess: newTestSession("buddy3"),
},
},
relayToScreenNameParams: relayToScreenNameParams{
{
screenName: "test-user",
message: wire.SNACMessage{
Frame: wire.SNACFrame{
FoodGroup: wire.Buddy,
SubGroup: wire.BuddyArrived,
},
Body: wire.SNAC_0x03_0x0B_BuddyArrived{
TLVUserInfo: newTestSession("buddy1").TLVUserInfo(),
},
},
},
{
screenName: "test-user",
message: wire.SNACMessage{
Frame: wire.SNACFrame{
FoodGroup: wire.Buddy,
SubGroup: wire.BuddyArrived,
},
Body: wire.SNAC_0x03_0x0B_BuddyArrived{
TLVUserInfo: newTestSession("buddy3").TLVUserInfo(),
},
},
},
},
feedbagParams: feedbagParams{
//{
// screenName: "test-user",
// results: []wire.FeedbagItem{},
//},
{
screenName: "buddy1",
results: []wire.FeedbagItem{},
},
{
screenName: "buddy3",
results: []wire.FeedbagItem{},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
feedbagManager := newMockFeedbagManager(t)
messageRelayer := newMockMessageRelayer(t)
for _, params := range tt.buddiesParams {
feedbagManager.EXPECT().
Buddies(params.screenName).
Return(params.results, nil)
}
for _, params := range tt.retrieveByScreenNameParams {
messageRelayer.EXPECT().
RetrieveByScreenName(params.screenName).
Return(params.sess)
}
for _, params := range tt.relayToScreenNameParams {
messageRelayer.EXPECT().
RelayToScreenName(mock.Anything, params.screenName, params.message)
}
for _, params := range tt.feedbagParams {
feedbagManager.EXPECT().
Feedbag(params.screenName).
Return(params.results, nil)
}

svc := NewFeedbagService(slog.Default(), messageRelayer, feedbagManager, nil)

haveErr := svc.Use(nil, tt.sess)
assert.ErrorIs(t, tt.wantErr, haveErr)
})
}
}
49 changes: 49 additions & 0 deletions foodgroup/mock_feedbag_manager_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 3 additions & 27 deletions foodgroup/oservice.go
Original file line number Diff line number Diff line change
Expand Up @@ -448,34 +448,10 @@ func (s OServiceServiceForBOS) HostOnline() wire.SNACMessage {
}

// ClientOnline runs when the current user is ready to join.
// It performs the following sequence of actions:
// - Announce current user's arrival to users who have the current user on
// their buddy list.
// - Send current user its buddy list.
// It announces current user's arrival to users who have the current user on
// their buddy list.
func (s OServiceServiceForBOS) ClientOnline(ctx context.Context, _ wire.SNAC_0x01_0x02_OServiceClientOnline, sess *state.Session) error {
if err := broadcastArrival(ctx, sess, s.messageRelayer, s.feedbagManager); err != nil {
return err
}

return s.retrieveOnlineBuddies(ctx, sess)
}

func (s OServiceServiceForBOS) retrieveOnlineBuddies(ctx context.Context, sess *state.Session) error {
buddies, err := s.feedbagManager.Buddies(sess.ScreenName())
if err != nil {
return err
}
for _, screenName := range buddies {
buddy := s.messageRelayer.RetrieveByScreenName(screenName)
if buddy == nil || buddy.Invisible() {
continue
}
if err := unicastArrival(ctx, buddy, sess, s.messageRelayer, s.feedbagManager); err != nil {
return err
}
}

return nil
return broadcastArrival(ctx, sess, s.messageRelayer, s.feedbagManager)
}

// NewOServiceServiceForChat creates a new instance of OServiceServiceForChat.
Expand Down
62 changes: 6 additions & 56 deletions foodgroup/oservice_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -739,7 +739,7 @@ func TestOServiceServiceForBOS_ClientOnline(t *testing.T) {
// bodyIn is the SNAC body sent from the arriving user's client to the
// server
bodyIn wire.SNAC_0x01_0x02_OServiceClientOnline
// buddyLookupParams contains params for looking up arriving user's
// buddiesParams contains params for looking up arriving user's
// buddies
buddyLookupParams buddiesLookupParams
// adjacentUsersParams contains params for looking up users who have
Expand All @@ -752,16 +752,16 @@ func TestOServiceServiceForBOS_ClientOnline(t *testing.T) {
// retrieveByScreenNameParams contains params for looking up the
// session for each of the arriving user's buddies
retrieveByScreenNameParams retrieveByScreenNameParams
// sendToScreenNameParams contains params for sending arrival
// relayToScreenNameParams contains params for sending arrival
// notifications for each of the arriving user's buddies to the
// arriving user's client
sendToScreenNameParams relayToScreenNameParams
relayToScreenNameParams relayToScreenNameParams
// feedbagParams contains params for retrieving a user's feedbag
feedbagParams feedbagParams
wantErr error
}{
{
name: "notify arriving user's buddies of its arrival and populate the arriving user's buddy list",
name: "notify arriving user's buddies of their arrival",
sess: newTestSession("test-user"),
bodyIn: wire.SNAC_0x01_0x02_OServiceClientOnline{},
interestedUsersParams: adjacentUsersParams{
Expand All @@ -784,61 +784,11 @@ func TestOServiceServiceForBOS_ClientOnline(t *testing.T) {
},
},
},
buddyLookupParams: buddiesLookupParams{
{
screenName: "test-user",
buddies: []string{"buddy1", "buddy3"},
},
},
retrieveByScreenNameParams: retrieveByScreenNameParams{
{
screenName: "buddy1",
sess: newTestSession("buddy1"),
},
{
screenName: "buddy3",
sess: newTestSession("buddy3"),
},
},
sendToScreenNameParams: relayToScreenNameParams{
{
screenName: "test-user",
message: wire.SNACMessage{
Frame: wire.SNACFrame{
FoodGroup: wire.Buddy,
SubGroup: wire.BuddyArrived,
},
Body: wire.SNAC_0x03_0x0B_BuddyArrived{
TLVUserInfo: newTestSession("buddy1").TLVUserInfo(),
},
},
},
{
screenName: "test-user",
message: wire.SNACMessage{
Frame: wire.SNACFrame{
FoodGroup: wire.Buddy,
SubGroup: wire.BuddyArrived,
},
Body: wire.SNAC_0x03_0x0B_BuddyArrived{
TLVUserInfo: newTestSession("buddy3").TLVUserInfo(),
},
},
},
},
feedbagParams: feedbagParams{
{
screenName: "test-user",
results: []wire.FeedbagItem{},
},
{
screenName: "buddy1",
results: []wire.FeedbagItem{},
},
{
screenName: "buddy3",
results: []wire.FeedbagItem{},
},
},
},
}
Expand All @@ -865,7 +815,7 @@ func TestOServiceServiceForBOS_ClientOnline(t *testing.T) {
RetrieveByScreenName(params.screenName).
Return(params.sess)
}
for _, params := range tt.sendToScreenNameParams {
for _, params := range tt.relayToScreenNameParams {
messageRelayer.EXPECT().
RelayToScreenName(mock.Anything, params.screenName, params.message)
}
Expand Down Expand Up @@ -920,7 +870,7 @@ func TestOServiceServiceForChat_ClientOnline(t *testing.T) {
// broadcastExcept contains params for broadcasting chat arrival to all
// chat participants except the user joining
broadcastExcept broadcastExcept
// sendToScreenNameParams contains params for sending chat room
// relayToScreenNameParams contains params for sending chat room
// metadata and chat participant list to joining user
sendToScreenNameParams sendToScreenNameParams
wantErr error
Expand Down
5 changes: 3 additions & 2 deletions server/oscar/handler/feedbag.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type FeedbagService interface {
RightsQuery(ctx context.Context, inFrame wire.SNACFrame) wire.SNACMessage
StartCluster(ctx context.Context, inFrame wire.SNACFrame, inBody wire.SNAC_0x13_0x11_FeedbagStartCluster)
UpsertItem(ctx context.Context, sess *state.Session, inFrame wire.SNACFrame, items []wire.FeedbagItem) (wire.SNACMessage, error)
Use(ctx context.Context, sess *state.Session) error
}

func NewFeedbagHandler(logger *slog.Logger, feedbagService FeedbagService) FeedbagHandler {
Expand Down Expand Up @@ -67,9 +68,9 @@ func (h FeedbagHandler) QueryIfModified(ctx context.Context, sess *state.Session
return rw.SendSNAC(outSNAC.Frame, outSNAC.Body)
}

func (h FeedbagHandler) Use(ctx context.Context, _ *state.Session, inFrame wire.SNACFrame, _ io.Reader, _ oscar.ResponseWriter) error {
func (h FeedbagHandler) Use(ctx context.Context, sess *state.Session, inFrame wire.SNACFrame, _ io.Reader, _ oscar.ResponseWriter) error {
h.LogRequest(ctx, inFrame, nil)
return nil
return h.FeedbagService.Use(ctx, sess)
}

func (h FeedbagHandler) InsertItem(ctx context.Context, sess *state.Session, inFrame wire.SNACFrame, r io.Reader, rw oscar.ResponseWriter) error {
Expand Down
4 changes: 4 additions & 0 deletions server/oscar/handler/feedbag_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,10 @@ func TestFeedbagHandler_Use(t *testing.T) {
}

svc := newMockFeedbagService(t)
svc.EXPECT().
Use(mock.Anything, mock.Anything).
Return(nil)

h := NewFeedbagHandler(slog.Default(), svc)
responseWriter := newMockResponseWriter(t)

Expand Down
Loading

0 comments on commit cc82a27

Please sign in to comment.