Browse Source

implement ALERT

Mike 2 years ago
parent
commit
0eb2662f71
5 changed files with 170 additions and 2 deletions
  1. 29 0
      oscar/snacs.go
  2. 25 0
      server/alert.go
  3. 102 0
      server/alert_test.go
  4. 10 2
      server/oservice.go
  5. 4 0
      server/protocol.go

+ 29 - 0
oscar/snacs.go

@@ -711,6 +711,35 @@ type SNAC_0x17_0x07_BUCPChallengeResponse struct {
 	AuthKey string `len_prefix:"uint16"`
 }
 
+//
+// 0x18: Alert
+//
+
+var (
+	AlertErr                       uint16 = 0x0001
+	AlertSetAlertRequest           uint16 = 0x0002
+	AlertSetAlertReply             uint16 = 0x0003
+	AlertGetSubsRequest            uint16 = 0x0004
+	AlertGetSubsResponse           uint16 = 0x0005
+	AlertNotifyCapabilities        uint16 = 0x0006
+	AlertNotify                    uint16 = 0x0007
+	AlertGetRuleRequest            uint16 = 0x0008
+	AlertGetRuleReply              uint16 = 0x0009
+	AlertGetFeedRequest            uint16 = 0x000A
+	AlertGetFeedReply              uint16 = 0x000B
+	AlertRefreshFeed               uint16 = 0x000D
+	AlertEvent                     uint16 = 0x000E
+	AlertQogSnac                   uint16 = 0x000F
+	AlertRefreshFeedStock          uint16 = 0x0010
+	AlertNotifyTransport           uint16 = 0x0011
+	AlertSetAlertRequestV2         uint16 = 0x0012
+	AlertSetAlertReplyV2           uint16 = 0x0013
+	AlertTransitReply              uint16 = 0x0014
+	AlertNotifyAck                 uint16 = 0x0015
+	AlertNotifyDisplayCapabilities uint16 = 0x0016
+	AlertUserOnline                uint16 = 0x0017
+)
+
 type SnacOServiceErr struct {
 	Code uint16
 }

+ 25 - 0
server/alert.go

@@ -0,0 +1,25 @@
+package server
+
+import (
+	"github.com/mkaminski/goaim/oscar"
+)
+
+func NewAlertRouter() AlertRouter {
+	return AlertRouter{}
+}
+
+type AlertRouter struct {
+}
+
+func (rt *AlertRouter) RouteAlert(SNACFrame oscar.SnacFrame) error {
+	switch SNACFrame.SubGroup {
+	case oscar.AlertNotifyCapabilities:
+		fallthrough
+	case oscar.AlertNotifyDisplayCapabilities:
+		// just read the request to placate the client. no need to send a
+		// response.
+		return nil
+	default:
+		return ErrUnsupportedSubGroup
+	}
+}

+ 102 - 0
server/alert_test.go

@@ -0,0 +1,102 @@
+package server
+
+import (
+	"bytes"
+	"github.com/mkaminski/goaim/oscar"
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+func TestAlertRouter_RouteAlert(t *testing.T) {
+	cases := []struct {
+		// name is the unit test name
+		name string
+		// input is the request payload
+		input XMessage
+		// output is the response payload
+		output XMessage
+		// handlerErr is the mocked handler error response
+		handlerErr error
+		// expectErr is the expected error returned by the router
+		expectErr error
+	}{
+		{
+			name: "receive AlertNotifyCapabilities, return no response",
+			input: XMessage{
+				snacFrame: oscar.SnacFrame{
+					FoodGroup: oscar.ALERT,
+					SubGroup:  oscar.AlertNotifyCapabilities,
+				},
+				snacOut: oscar.SnacFrame{},
+			},
+			output: XMessage{},
+		},
+		{
+			name: "receive AlertNotifyDisplayCapabilities, return no response",
+			input: XMessage{
+				snacFrame: oscar.SnacFrame{
+					FoodGroup: oscar.ALERT,
+					SubGroup:  oscar.AlertNotifyDisplayCapabilities,
+				},
+				snacOut: oscar.SnacFrame{},
+			},
+			output: XMessage{},
+		},
+		{
+			name: "receive AlertGetSubsRequest, expect ErrUnsupportedSubGroup",
+			input: XMessage{
+				snacFrame: oscar.SnacFrame{
+					FoodGroup: oscar.ALERT,
+					SubGroup:  oscar.AlertGetSubsRequest,
+				},
+				snacOut: struct{}{}, // empty SNAC
+			},
+			output:    XMessage{}, // empty SNAC
+			expectErr: ErrUnsupportedSubGroup,
+		},
+	}
+
+	for _, tc := range cases {
+		t.Run(tc.name, func(t *testing.T) {
+			router := AlertRouter{}
+
+			bufIn := &bytes.Buffer{}
+			assert.NoError(t, oscar.Marshal(tc.input.snacOut, bufIn))
+
+			bufOut := &bytes.Buffer{}
+			seq := uint32(1)
+
+			err := router.RouteAlert(tc.input.snacFrame)
+			assert.ErrorIs(t, err, tc.expectErr)
+			if tc.expectErr != nil {
+				return
+			}
+
+			if tc.output == (XMessage{}) {
+				// make sure no response was sent
+				assert.Empty(t, bufOut.Bytes())
+				return
+			}
+
+			// verify the FLAP frame
+			flap := oscar.FlapFrame{}
+			assert.NoError(t, oscar.Unmarshal(&flap, bufOut))
+
+			// make sure the sequence number was incremented
+			assert.Equal(t, uint32(2), seq)
+
+			flapBuf, err := flap.SNACBuffer(bufOut)
+			assert.NoError(t, err)
+
+			// verify the SNAC frame
+			snacFrame := oscar.SnacFrame{}
+			assert.NoError(t, oscar.Unmarshal(&snacFrame, flapBuf))
+			assert.Equal(t, tc.output.snacFrame, snacFrame)
+
+			// verify the SNAC message
+			snacBuf := &bytes.Buffer{}
+			assert.NoError(t, oscar.Marshal(tc.output.snacOut, snacBuf))
+			assert.Equal(t, snacBuf.Bytes(), flapBuf.Bytes())
+		})
+	}
+}

+ 10 - 2
server/oservice.go

@@ -103,7 +103,15 @@ func (s OServiceService) WriteOServiceHostOnline(w io.Writer, sequence *uint32)
 		SubGroup:  oscar.OServiceHostOnline,
 	}
 	snacPayloadOut := oscar.SNAC_0x01_0x03_OServiceHostOnline{
-		FoodGroups: []uint16{oscar.OSERVICE, oscar.LOCATE, oscar.BUDDY, oscar.ICBM, oscar.FEEDBAG, oscar.CHAT_NAV},
+		FoodGroups: []uint16{
+			oscar.ALERT,
+			oscar.BUDDY,
+			oscar.CHAT_NAV,
+			oscar.FEEDBAG,
+			oscar.ICBM,
+			oscar.LOCATE,
+			oscar.OSERVICE,
+		},
 	}
 	return writeOutSNAC(oscar.SnacFrame{}, snacFrameOut, snacPayloadOut, sequence, w)
 }
@@ -201,7 +209,7 @@ func (s OServiceService) UserInfoQueryHandler(sess *Session) XMessage {
 
 func (s OServiceService) ClientOnlineHandler(snacPayloadIn oscar.SNAC_0x01_0x02_OServiceClientOnline, sess *Session, sm SessionManager, fm FeedbagManager, room ChatRoom) error {
 	for _, version := range snacPayloadIn.GroupVersions {
-		fmt.Printf("hahaha ClientOnlineHandler read SNAC client messageType: %+v\n", version)
+		fmt.Printf("ClientOnlineHandler read SNAC client messageType: %+v\n", version)
 	}
 	if err := BroadcastArrival(sess, sm, fm); err != nil {
 		return err

+ 4 - 0
server/protocol.go

@@ -285,6 +285,7 @@ func ReadBos(cfg Config, sess *Session, seq uint32, sm SessionManager, fm *Feedb
 
 func NewRouter() Router {
 	return Router{
+		AlertRouter:    NewAlertRouter(),
 		BuddyRouter:    NewBuddyRouter(),
 		ChatNavRouter:  NewChatNavRouter(),
 		ChatRouter:     NewChatRouter(),
@@ -302,6 +303,7 @@ func NewRouterForChat() Router {
 }
 
 type Router struct {
+	AlertRouter
 	BuddyRouter
 	ChatNavRouter
 	ChatRouter
@@ -329,6 +331,8 @@ func (rt *Router) routeIncomingRequests(cfg Config, sm SessionManager, sess *Ses
 		return routeBUCP()
 	case oscar.CHAT:
 		return rt.RouteChat(sess, sm, snac, buf, rw, sequence)
+	case oscar.ALERT:
+		return rt.RouteAlert(snac)
 	default:
 		return ErrUnsupportedFoodGroup
 	}